文章目录
- 一、内部类概述
- 二、需要了解的内部类
- 2.1 静态内部类
- 2.2 成员内部类
- 2.3 局部内部类
- 2.4 面试笔试题
- 三、匿名内部类 ★
- 四、Lambda表达式 ★
- 4.1 Lambda 表达式的概述
- 4.2 Lambda 表达式的省略规则
- 4.3 Lambda 的使用
一、内部类概述
内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。如 People 类和 Heart 类:
场景: 当一个事物的内部,还有一个部分需要一个完整的结构进行描述时。
作用: 内部类通常可以方便访问外部类的成员,包括私有的成员;内部类提供了更好的封装性,内部类本身就可以用 private protected 等修饰,封装性可以做更多控制。
注: 外部类不能使用 protected 和 private 修饰。
二、需要了解的内部类
2.1 静态内部类
有static修饰,属于外部类本身。
它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已。
静态内部类创建对象的格式:
拓展:
1. 静态内部类中是否可以直接访问外部类的静态成员?
可以,外部类的静态成员只有一份可以被共享访问。
2. 静态内部类中是否可以直接访问外部类的实例成员?
不可以,外部类的实例成员必须用外部类对象访问。
总结: 静态内部类可以直接访问外部类的静态成员,不能直接访问外部类的实例成员。
2.2 成员内部类
无 static 修饰,属于外部类的对象。
JDK 16之前,成员内部类中不能定义静态成员,JDK 16开始也可以定义静态成员了。
成员内部类创建对象的格式:
拓展:
1. 成员内部类中是否可以直接访问外部类的静态成员?
可以,外部类的静态成员只有一份可以被共享访问。
2. 成员内部类的实例方法中是否可以直接访问外部类的实例成员?
可以,因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。
总结: 可以直接访问外部类的静态成员,实例方法中可以直接访问外部类的实例成员。
2.3 局部内部类
局部内部类放在方法、代码块、构造器等执行体中。
局部内部类的类文件名为: 外部类$N内部类.class,N 代表索引,从 1 开始。
2.4 面试笔试题
请观察如下代码,写出合适的代码对应其注释要求输出的结果。
class People{
private int heartbeat = 150;
/**
成员内部类
*/
public class Heart{
private int heartbeat = 110;
public void show(){
int heartbeat = 78;
System.out.println(heartbeat); // 78
System.out.println(this.heartbeat); // 110
System.out.println(People.this.heartbeat); // 150
System.out.println(new People().heartbeat); // 150
}
}
}
注意: 在成员内部类中访问所在外部类对象 ,格式:外部类名.this
。
三、匿名内部类 ★
本质上是一个没有名字的局部内部类,定义在方法中、代码块中等。
作用: 方便创建子类对象,最终目的为了简化代码编写。
格式:
如:new Employee{… }
, {…}
就是匿名内部类。
特点总结:
① 匿名内部类是一个没有名字的内部类。
② 匿名内部类写出来就会产生一个匿名内部类的对象。
③ 匿名内部类的对象类型相当于是当前 new 的那个的类型的子类类型。
举例:
interface Swimming{
void swim();
}
public class Test {
public static void main(String[] args) {
Swimming ss = new Swimming() {
@Override
public void swim() {
System.out.println("老师游泳!");
}
};
go(ss); // 老师游泳!
// 匿名内部类可以作为方法的实际参数进行传输
go(new Swimming() {
@Override
public void swim() {
System.out.println("学生游泳!");
}
}); // 学生游泳!
}
public static void go(Swimming s){
s.swim();
}
}
对比写一个学生类和老师类实现 Swimming 接口,匿名内部类使得代码更加的简化。
四、Lambda表达式 ★
4.1 Lambda 表达式的概述
Lambda 表达式是 JDK 8 开始后的一种新语法形式,其用于简化匿名内部类的代码写法。
格式:
注意:Lambda 表达式只能简化函数式接口的匿名内部类的写法形式。
函数式接口:首先必须是接口(抽象类不可以)其次接口中有且仅有一个抽象方法的形式。
通常函数式接口用 @FunctionalInterface
注解进行标注,起到只能定义一个抽象方法的限制作用。
4.2 Lambda 表达式的省略规则
-
参数类型可以省略不写。
-
如果只有一个参数,参数类型可以省略,同时 () 也可以省略。
-
如果 Lambda 表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号 “;”
-
如果 Lambda 表达式的方法体代码只有一行代码。可以省略大括号不写。
此时,如果这行代码是 return 语句,必须省略 return,同时也必须省略 “;”。
4.3 Lambda 的使用
举例:自定义数组的排序规则,让其降序排序。
sort(T[] a, Comparator<? super T > c);
参数一: 被排序的数组 必须是引用类型的元素。 参数二: 代表了一个比较器对象。
该比较器对象是一个函数式接口,因此有两种方案,一种是匿名内部类的形式,另一种是比较类实现该接口的方法。
注: ? super T
:代表泛型下限,?
必须是 T
或者其父类。
方式一:匿名内部类
public class Test2 {
public static void main(String[] args) {
// 定义数组
Integer[] ages = {34, 12, 42, 23};
// 匿名内部类
Arrays.sort(ages, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1; // 降序
}
});
// 简化形式 1
Arrays.sort(ages, (Integer o1, Integer o2) ->{
return o2 - o1;
});
// 简化形式 2
Arrays.sort(ages, (o1, o2) ->{
return o2 - o1;
});
// 简化形式 3
Arrays.sort(ages, (o1, o2) -> o2 - o1 );
}
}
方式二:子类实现
public class Test2 {
public static void main(String[] args) {
Integer[] ages = {34, 12, 42, 23};
Arrays.sort(ages, new Comp());
System.out.println(Arrays.toString(ages)); // [42, 34, 23, 12]
}
}
class Comp implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}
文章参考:Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题)