不管是静态内部类还是非静态内部类,它们声明变量的语法完全一样。区别只是在创建内部类对象时,静态内部类只需要外部类调用构造器,而非静态内部类必须使用外部类对象来调用构造器。
如果把一个内部类放在方法里定义,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效,由于局部内部类不能再外部类的方法以外的地方使用,因此局部内部类也不能使用访问控制符和static修饰符修饰。
对于局部成员而言,不管是局部变量还是局部内部类,它们的上一级程序单元都是方法而不是类,使用static修饰它们没有任何意义。因此,所有局部成员都不能使用static修饰。不仅如此,因为局部成员的作用域时所在方法,其他程序单元永远也不可能访问另一个方法中的局部成员,所以所有的局部成员都不能使用访问控制符修饰。
匿名内部类适合创建那种只需要一次使用的类,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。匿名内部类必须继承一个父类,或者实现一个接口,但最多只能继承一个父类,或实现一个接口
匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此不允许将匿名内部类定义成抽象类。
匿名内部类不能定义构造器。由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义初始化块,可以通过实例化初始化块来完成构造器需要完成的事情。
由于匿名内部类不是抽象类,所以匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法。
当通过实现接口来创建匿名内部类时,匿名内部类也不能显式创建构造器,因此匿名内部类只有一个隐式的无参数构造其,故new接口后面的括号里不能传入参数值。但如果铜鼓继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,此处的相似指的是拥有相同的形参列表。
Java要求被局部内部类、匿名内部类访问的局部变量必须使用final修饰,从Java8之后,如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰符。
Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用更加简洁的代码块来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例。
当使用Lambda表达式代替代替匿名内部类创建对象时,Lambda表达式代码块的代码块将会代替实现抽象方法的方法体,Lambda表达式就相当于一个匿名方法。Lambda表达式的主要作用就是代替匿名匿名内部类的烦琐语法。
Lambda表达式的的组成:
形参列表:形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号都可以省略。(形参时接口中方法的形参)
箭头:->
代码块:Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而他的代码块仅有一条省略了return的语句,Lambda表达式会自动返回这条语句。(重写接口中的代码块)
Lambda表达式实际上将会被当成一个“任意类型”的对象,到底要当成何种类型的对象,这取决于运行环境的需要。
Lambda表达式类型,也被称为目标类型,Lambda表达式的目标类型必须是函数式接口。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
Java8专门为函数式接口提供了@FunctionalInterface注解,该注解通常放在接口定义前面,该注解对程序功能没有任何作用,它用于告诉编辑器执行更严格的检查——检查该接口必须是函数式接口,否则编译器就会报错。判断接口是否是函数式接口。
Lambda表达式实现的是匿名方法(只有方法体,没有方法名)——因此它只能实现特定函数式接口中的唯一方法。这意味着Lambda表达式有两个限制:
Lambda 表达式的目标类型必须是明确的函数式接口
Lambda表达式只能为函数式接口创建对象。Lambda表达式只能实现一个方法,因此他只能为只有一个抽象方法的接口创建对象。
自己理解:Lambda表达式是接口实现的方式之一,接口实现可通过接口实现类、匿名内部类、Lambda表达式。其中Lambda表达式最为简洁。Lambda对实现的接口有要求,因为Lambda表达式方法体本质是一个匿名方法,一个匿名方法只能实现接口中的一个方法。所以要求实现的接口是函数式接口。
函数式接口:要求实现类实现的抽象方法有且只有一个。
同样的Lambda表达式的目标类型完全可能是变化的——唯一的要求是,Lambda表达式实现的匿名方法与目标类型中唯一的抽象方法具有相同的形参列表。
如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。方法引用和构造器引用可以让Lambda表达式的代码更加简洁。
int i = indexof(String str):返回指定字符串在此字符串中第一次出现的索引。
引用类方法:
类名::类方法,函数式接口中被实现方法的全部参数传给该类方法作为参数。
引用特定对象的实例方法:
特定对象::实例方法,函数式接口中被实现方法的全部参数传给该方法作为参数。
引用某类对象的实例方法:
类名::实例方法,函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数
引用构造器:
类名::new,函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数
Lambda表达式创建的对象和匿名内部类创建的对象一样,都可以直接调用从接口中继承的默认方法。
与匿名内部类类似,由于Lambda表达式访问了局部变量,因此该局部变量相当于有一个隐式的final修饰,因此同样不允许对局部变量重新赋值。
Lambda表达式与匿名内部类的区别:
匿名内部类可以为任何接口创建实例——不管接口包含多少抽象方法,只要匿名内部类实现了所有的抽象方法即可;但Lambda表达式只能为函数式接口创建实例
匿名内部类可以为抽象类甚至普通类创建实例;但Lambda表达式只能为函数式接口创建实例。
匿名内部类实现的抽象方法的方法体允许调用接口中的默认方法;但Lambda表达式的代码块调用接口中定义的默认方法。
枚举类:一个类的对象是有限而且固定的。
枚举类是一个特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或多个接口,也可以定义自己的构造器。一个Java源文件只能定义一个public访问权限的枚举类,且该Java源文件也必须和该枚举类的类名相同。
枚举类与普通类的区别:
枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能显式继承其他父类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口。
使用enum定义非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类。
枚举类的构造器只能使用priva访问控制符。
枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例。
枚举类默认提供一个values()方法,该方法可以很方便的遍历所有枚举值。
任何一个类都有一个Class对象,它保存着实例化该类所需要的基本信息,A.class 返回的A的Class对象
枚举类的实例只能是枚举值,而不是随意的通过new来创建枚举类对象。
建议将枚举类的成员变量都使用private final修饰,所以枚举类显式定义带参数的构造器,一旦为枚举类显式定义了带参数的构造器,列出枚举值时就必须对应的传入参数。
在枚举类中列出枚举值时,实际上就是调用构造器创建枚举类对象,只是这里无需使用new关键字,也无须显式调用构造器。
并不是所有的枚举类都使用类final修饰,非抽象的枚举类才默认使用final修饰,对于一个抽象的枚举类而言——只要它包含了抽象方法,他就是抽象枚举类,系统会默认使用abstract修饰。
枚举类里定义抽象方法时不能使用abstract关键字将枚举类定义成抽象类,但因为枚举类需要显式创建枚举值,而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现。自己理解:枚举值是由匿名内部子类创建,匿名内部子类作用相当于抽象枚举类的子类,匿名内部子类能够创建实例,表明匿名内部子类全部实现了枚举类的抽象方法。