本文共 7803 字,大约阅读时间需要 26 分钟。
public void test(String str, String... args) { //jvm会预先创建一个数组来接收个数可变的参数,即使传了0个参数,args也不为null //可根据数组的元素个数来判断是否传入了参数 if(args.length!=0){ for (String arg : args) { //... } }}
面向对象的3大特性:封装、继承、多态
继承
在子类的构造函数中,可以
super(xxx); //调用父类带参的构造函数,必须放在方法体的第一行
如果在构造函数中没有显式调用父类的构造函数,会在执行构造函数之前自动调用父类无参的构造函数。
重写父类的方法
在子类中访问父类的成员
super(xxx) //调用父类的构造函数super.xxx //访问父类的成员变量(要有权限才可以)super.xxx(xxx) //调用父类的成员方法
创建对象时,会先初始化这个类所有的父类。
多态
引用型变量有2个类型:编译时类型、运行时类型。
编译时类型是声明该变量时的类型,运行时类型是实际赋给该变量的类型。
多态:运行时类型可以不一致。
public void f(User user) { }
编译时类型是User,假设User有2个子类Student、Teacher,运行时可以传入Student、Teacher类的对象,类型不一致,即多态。
判断某个引用型变量是否是某个类 | 接口的实例
User user = new User();if(user instanceof User){ //返回boolean值}
变量 instanceof 类 | 接口名,变量要声明为该类|接口类型,否则通不过编译。
内部类
类中可以嵌套其它类
class Outer { //...... class Inner { //....... } }
匿名内部类没有类名,只能使用一次,用来实现接口、抽象类,会在该处创建匿名内部类的一个实例
new 接口|抽象类{ }
抽象类
public abstract class Xxx { //..... public abstract String Xxx();}
枚举常用于表示同一种类的常量集合,比如订单状态、用户角色、职称等。
以前的方式
public class Role { public static final String COMMON = "common"; public static final String VIP = "vip"; public static final String SVIP = "svip";}
枚举
//成员变量+私有构造器+getter方法public enum Role { //实质是调用构造函数创建当前类的实例,作为类的静态成员 COMMON("common"), VIP("vip"), SVIP("svip"); private String role; private Role(String role) { this.role = role; } public String getRole() { return this.role; }}//可以不设置成员变量public enum Role { COMMON, VIP, SVIP;}
枚举在编译后会产生一个.class文件,使用javap反编译.class文件得到的java代码如下
public final class Role extends Enum{ public static final Role COMMON; public static final Role VIP; public static final Role SVIP; public static Role[] values(); public static Role valueOf(String); public String getRole(); static { }}
public interface Xxx { // 成员变量默认使用public static final修饰,即常量,在声明时就必须指定初始值 String XXX = ""; // 方法默认使用public abstract修饰 String xxx(); // 可以定义默认方法,但必须要有方法体 default void test2() { } //接口中可以定义静态方法方法,但必须要有方法体,默认使用(也只能使用)public修饰 static void test3() { } // 内部也可以定义类、接口、接口,均默认使用(也只能使用)public static修饰 // 不能有构造函数、初始化块、私有方法}
// 类只支持单继承,接口支持多继承class 类A extends 类B { }interface 接口A extends 接口B, 接口C { }// 一个类可以实现多个接口class 类A implements 接口A, 接口B { }// 类在继承的同时可以实现接口class 类A extends 类B implements 接口A, 接口B { }
接口、抽象类的区别
一般把子类共有的成员变量、实现相同的公共方法抽象出来放在抽象类中,实现不同的公共方法也可以作为抽象方法放在抽象类中;子类中公共的常量,以及实现不同的公共方法,可以放在接口中。
泛型可以限制对象|元素类型,在编译时对数据类型进行校验。
//在普通的成员变量中使用泛型public class Test{ private T t; public Test(T t) { this.t = t; } public T getT() { return t; } public void setT(T t) { this.t = t; } }
//静态成员字段不能使用泛型,但静态方法的返回值类型、参数类型可以使用泛型class Test { //需要在返回值类型前用< >进行声明 public staticT test(T t) { return t; } }
//类名后面标注的泛型可以用extend指定上边界,但不能用super指定下边界public class Test{ }//可以使用多个泛型public class Test { }public class Test { //使用泛型声明变量、返回值类型时,可以用? extend指定上边界,必须要是Number的子类 public Test handler1(Test test) { return test; } //可以用? super声明下边界,必须要是Integer的父类 public void handler2(Test test) { } //上边界、下边界都包含边界本身 }
注意点
1、使用new关键字调用构造方法,可调用任意构造方法。
2、通过反射创建实例
实质都是调用构造方法创建实例。
try { //实质是调用无参构造器创建实例 User user = User.class.newInstance();} catch (InstantiationException e) { e.printStackTrace();} catch (IllegalAccessException e) { e.printStackTrace();}
try { //通过class对象获取要使用的构造器,通过构造器创建实例 ConstructornoArgConstructor = User.class.getConstructor(); User user1 = noArgConstructor.newInstance(); Constructor allArgConstructor = User.class.getConstructor(Integer.class, String.class, String.class); User user2 = allArgConstructor.newInstance(1, "chy", "188xxxxxx");} catch (NoSuchMethodException e) { e.printStackTrace();} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { e.printStackTrace();}
3、使用对象的clone()方法创建实例,需要重写Object类的clone()方法
@Data@AllArgsConstructor@NoArgsConstructorpublic class User { private Integer id; private String name; private String tel; /** * Object类的clone()方法是直接抛出异常 CloneNotSupportedException * 需要重写Object类的clone()方法,把访问权限改为public,可以把返回值类型改为具体类型,避免使用时强转 */ @Override public User clone() { User user = new User(); user.setId(this.id); user.setName(this.name); user.setTel(this.tel); return user; }}
User user = new User(1, "chy", "188xxxxx");User user1 = user.clone();
4、使用反序列化创建对象,前提是该类要是可序列化的,比如implements Serializable。
前2种实质都是调用构造方法创建实例,后2种则不是。
java的异常体系
try{ int i = 1 / 0;}catch (Exception e){ System.out.println("catch...");}finally { System.out.println("finally...");}System.out.println("other...");
try中没有发生异常:try -> finally -> finally之后的代码
try中发生了异常:
在方法体中没有捕获处理异常,会跳出该方法的栈帧,导致方法体中后续代码都不会执行。
在循环体中,慎用throw直接抛出异常,如果循环体中的异常直接抛到了循环体外,会导致循环终止,后续批次都不会执行。
比如使用循环批量推送短信,某次循环时发生异常,没有在循环内处理异常,让异常抛到了循环外,则循环会被终止,循环中后续批次的消息都不会发送。
循环这种批量操作的场景,往往要尽量避免循环被异常终止,保证循环的继续执行,在循环体中可能发生异常的地方使用try…catch捕获处理掉异常。
可以extends Exception实现自定义的异常类。
final可修饰类、变量(包括成员变量、局部变量)、方法
初始化块和成员变量、成员函数一个级别,一般用于初始化。
class Xxx { // 普通初始化块 { } // 静态初始化块 static { } // ......}
一个类中可以有多个初始化块,如果都是static初始化块或者普通初始化块,越靠前的越先执行。
static初始化快只能访问类的静态成员,只在JVM加载类的class对象时执行1次,常用于初始化类的静态成员、公共资源。
编译时,会把普通初始化块中的代码放到构造函数函数体内的最前面。每次创建对象时,在执行构造函数之前,都会先执行普通初始化快。普通初始化快常用与初始化对象。
函数式接口:接口中的抽象方法有且只有1个。可以有其它成员,但抽象方法必须要有且只能有1个。
lambda表达式是函数式接口的匿名内部类实现的语法糖,可以代替函数式接口的匿名内部类,只能代替函数式接口的匿名内部类,不能代替其它的匿名内部类。
@FunctionalInterface //声明为函数式接口interface FI { void out(String str1,String str2);}class T{ // lambda表达式 private FI fi = (str,str2) -> { //参数表直接写参数名,不写类型 System.out.println(str + " " + str2); }; }
lambda表达式用于实现函数式接口、并在该处创建一个实例,和匿名内部类相似,但匿名内部类可以实现任何抽象类、接口,lambda表达式只能实现函数式接口。
lambda表达式语法
(形参表) -> { //形参表不写参数类型,直接写参数名 //......}
lambda表达式其实就是函数式接口中抽象方法的实现。
只有一个形参时,可以缺省();函数体只有一条语句,且该语句是return语句时,可以缺省{ }、关键字return。
转载地址:http://egqlb.baihongyu.com/