009-面相对象高级-2
面向对象高级
代码块
-
代码块是类的五大成分之一(成员变量、构造器、方法、代码块、内部类)。
-
代码块分两种
-
静态代码块:
-
格式
static { }
-
特点:类加载时自动执行,与类一起优先加载,由于类只会加载一次,所以静态代码块也只会执行一次。
-
作用:完成类的初始化,例如:对静态变量的初始化赋值。
-
举例:
- 类似静态的普通变量可以在静态代码块中赋值,也可以在定义的那一行赋值
- 但对于像new了一个静态数组,那么这个数组还需要初始化,就需要放在静态代码块中初始化
- 这么做的好处是保证静态变量只会被赋值一次,如果放在其他方法中,会导致每次调用都会被重新赋值一次
-
这是锦上添花的操作而不是必要的操作
-
-
实例代码块(构造代码块):
-
格式:
{ }
-
特点:每次创建对象时,执行实例代码块,并在构造器前执行。
-
作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化
-
-
内部类
- 如果一个类定义在另一个类内部,那么这个类就是内部类
- 例子:比如汽车类中定义一个发动机类
成员内部类
-
内部类语法
public class Outer { // 成员内部类:无static修饰,属于外部类的 *对象* 特有的 public class Inner { public void show() { System.out.println("show"); } } }
public class Test { public static void main(String[] args) { // 成员内部类创建对象的格式: // 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称(); Outer.Inner oi = new Outer().new Inner(); } }
-
成员内部类访问外部类成员的特点(拓展)
-
成员内部类可以直接访问外部类的静态成员(包括静态变量和静态方法),也可以直接访问外部类的实例成员
-
成员内部类的实例方法中,可以直接拿到当前寄生的外部类对象:
// 调内部类自己的内容 this // 调外部类的内容 外部类名.this
-
静态内部类
-
有static此时的内部类,属于外部类本身持有
public class Outer { // 成员内部类:无static修饰,属于外部类的 *本身* 特有的 public static class Inner { public void show() { System.out.println("show"); } } }
public class Test { public static void main(String[] args) { // 静态内部类创建对象的格式: // 外部类名.内部类名 对象名 = new 外部类名.内部类名(); Outer.Inner inner = new Outer().Inner(); inner.show(); } }
-
静态内部类中是否可以直接访问外部类中的静态成员?
- 可以
-
静态内部类中是否可以直接访问外部类中的实例成员?
- 不可以
- 外部类的实例成员属于外部类创建的对象,而静态内部类是通过外部类点内部类创建的;对于成员内部类来说,成员内部类是通过外部类的对象点内部类对象的当时创建的,可以访问到外部类的实例对象。
局部内部类
-
局部内部类是定义在方法中、代码块中、构造器等执行体中。
-
语法:
public class Test { public static void main(String[] args) { } public static void go() { class A { } abstract class B { } interface C { } } }
-
鸡肋语法,没什么用法,为匿名内部类做铺垫
匿名内部类(重点)
-
是一种特殊的局部内部类
-
所谓匿名:指的是程序员不需要为这个类声明名字,默认有个隐藏的名字
-
语法(注意最后的分号):
/** * new 类或接口(参数值...) { * 类体(一般是方法重写); * } */ new Animal() { @Override public void cry() { } };
-
特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象
-
作用:用于更方便的创建一个子类对象
-
语法举例:用在抽象类等处创建对象时,可以直接使用匿名内部类来创建对象
public abstract class Animal { public abstract void cry(); }
public class Test { public static void main(String[] args) { Animal a = new Animal() { @Override public void cry() { System.out.println("(>^ω^<)喵"); }; a.cry } } }
-
编译后的大概样子:
- 可以看的出本质就是个子类
-
匿名内部类在开发中的常见形式
-
通常作为一个对象参数传输给方法
-
举例:
-
原本需要定义实现类:
-
匿名内部类可以直接创建对象时使用:
-
-
-
匿名内部类应用场景示例
-
调用别人提供的方法实现需求时,这个方法真好可以让我们传输一个匿名内部类对象给其使用。
-
示例1:在按钮监听器中使用匿名内部类
-
示例2:使用comparator接口的匿名内部类对数组进行排序
-
@Data
会给类重写toString()
函数式编程(jdk8新增特性)
- 此“函数”类似于数学中的函数(强调做什么),只要输入的数据一致返回的结果也是一致的
- 数学中的函数实例:2x+1
- Java中的函数(Lambda表达式):
(x)->2x+1
- 函数式编程解决了什么问题?
- 使用Lambda函数替代某些匿名内部类对象,从而让程序代码更简洁,可读性更好。
- 注意:C语言中函数类似Java中的方法,而Java中函数是Lambda表达式
Lambda表达式
-
JDK 8开始新增的一种语法形式,它表示函数。
-
可以用于替代某些匿名内部类对象,从而让程序更简洁,可读性更好。
(被重写方法的形参列表)->{ 被重写方法的方法体代码 }
-
示例以及讲解:
-
已有Animal类
public abstract class Animal { public abstract void cry(); }
-
错误示范:
// 如下代码会报错 Animal a1 = () -> { System.out.println("猫是:(>^ω^<)喵地叫"); };
- Lambda并不是可以简化全部的匿名内部类,Lambda只能简化函数式接口(只有一个抽象方法的接口)的匿名内部类。
-
正确示范:
-
定义一个函数式接口
@FunctionalInterface // 声明函数式接口的注解。 interface Swim { void swimming(); }
-
简化前
Swim s1 = new Swim() { @Override public void swimming() { System.out.println("学生游泳贼快"); } };
-
简化后
Swim s1 = () -> { System.out.println("学生游泳贼快"); }; s1.swimming();
-
-
原理:Lambda表达式可以进行上下文推断
-
在如下代码中,上文指的是
Swim s1
,下文指的是接口中唯一一个抽象方法,因为是唯一抽象方法,所以能推断出要实现Swim中的那个唯一抽象方法。Swim s1 = () -> { System.out.println("学生游泳贼快"); };
-
-
将来我们见到的大部分函数式接口,上面都可能会有一个@FunctionalInterface的注解,该注解用于约束当前接口必须是函数式接口
-
-
Lambda的省略规则
-
参数类型全部可以省略不写
-
省略前:
Arrays.sort(students, (Student o1, student o2) -> { return o1.getAge() - o2.getAge();// 按年龄升序 });
-
省略后:
Arrays.sort(students, (o1, o2) -> { return o1.getAge() - o2.getAge();// 按年龄升序 });
-
-
如果只有一个参数,参数类型省略的同时“
()
”也可以省略,但多个参数不能省略“()
” -
如果Lambda表达式中只有一行代码,大括号可以不写,同时要省略分号“
;
”,如果这行代码是return
语句,也必须去掉return
-
省略前
Arrays.sort(students, (o1, o2) -> { return o1.getAge() - o2.getAge();// 按年龄升序 });
-
省略后
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge()// 按年龄升序 );
-
-
方法引用
静态方法引用
-
语法:
类名::静态方法
-
使用场景
-
如果某个Lambda表达式里只是调用一个静态方法,并且“
->
”前后参数的形式一致,就可以使用静态方法引用。-
假设Student类中有静态方法如下:
public static int compareByArray(Student o1, Student o2) { return o1.getAge() - o2.getAge(); }
-
简化前:
Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());
-
简化一步(
(o1, o2)
和Student.compareByArray(o1, o2)
参数形式一致):Arrays.sort(students, (o1, o2) -> Student.compareByArray(o1, o2));
-
使用静态方法引用简化:
Arrays.sort(students, Student::compareByArray);
-
-
实例方法引用
-
语法:
对象名::实例方法
-
使用场景:
-
如果某个Lambda表达式里只是通过对象名称调用一个实例方法,并且"
->
"前后参数的形式一致,就可以使用实例方法引用。-
假设Student类中有实例方法如下:
public static int compareByHeight(Student o1, Student o2) { return Double.compare(o1.getHeight(), o2.getHeight()); }
-
现在创建了Student对象
Student t = new Student();
-
简化前:
Arrays.sort(students, (o1, o2) -> o1.getHeight() - o2.getHeight());
-
简化一步:
Arrays.sort(students, (o1, o2) -> t.compareByHeight(o1, o2));
-
使用实例方法引用简化
Arrays.sort(students, t::compareByHeight);
-
-
特定类的方法引用
-
语法:
特定类的名称::方法
-
使用场景
- 如果某个Lambda表达式里只是调用一个特定类型的实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是座位该实例方法的入参的,则此时就可以使用特定类型的方法引用
-
示例及讲解:
Arrays.sort(names, (o1, o2) -> o1.compareToIgnoreCase(o2) );
-
上述代码可以简化为:
Arrays.sort(names, String::compareToIgnoreCase);
-
构造器引用
-
语法:
类名::new
-
使用场景:
- 如果某个Lambda表达式里只是在创建对象,并且“
->
”前后参数情况一致,就可以使用构造器引用。
- 如果某个Lambda表达式里只是在创建对象,并且“
-
示例及讲解:
-
有汽车工厂类:
-
下图注释的代码即为简化过程
-
常用API
String
-
String是什么,有什么用?
- String代表字符串,它的对象可以封装字符串数据,并提供了很多方法完成对字符串的处理。
-
String创建字符串对象的方式(引号和new对象两种)
- 两种方式有何区别?
- 只要是以双引号
"..."
方式写出的字符串对象,会存储到堆中的字符串常量池,且相同内容的字符串只存储一份。相当于创建多个内容相同字符串时,地址在栈中,指向堆里字符串常量池里唯一的那个字符串。对多个引号内容相同的字符串进行比较结果是相等的。 - 通过
new
方式创建字符串对象,每new
一次都会产生一个新的对象放在堆内存中。相当于将字符存到堆中后,每创建一个对象都会以这些字符组成一个字符串形成一个新的对象,此时对多个内容一样的new
出来的对象进行比较,得到的结果是不一致。
- 只要是以双引号
- 两种方式有何区别?
-
String提供的常用方法
-
注意:
- “
==
“号默认对地址进行比较,所以对String的比较使用equals方法
- “
ArrasyList集合
-
什么是集合?
-
集合是一种容器,用来装数据的,类似于数组
-
是泛型类,可以规定其中的数据类型
ArrayList<String> list = new ArrayList<>();
-
-
和数组有什么区别?
- 数组定义完成并启动后,长度就固定了
- 集合大小可变,功能丰富,开发中用的更多
GUI编程
- GUI,全称Graphical User Interface,是指图形用户界面。
- 通过图形元素(如窗口、按钮、文本框等)与用户进行交互。
- 与命令行界面(CLI)相比,GUI更加直观、友好。
Java的GUI编程包(企业对这个没有要求)
-
AWT(Abstract Window Toolkit)提供了一组原生的GUI组件,依赖于操作系统的本地窗口系统
-
Swing
-
基于AWT,提供了更丰富的GUI组件,轻量级组件,不依赖于本地窗口系统。
-
常用的Swing组件(AWT组件和Swing组件只有个J字母开头的差别)
-
直接让AI生成代码
-
事件处理
-
GUI编程中,事件的处理是通过事件监听器(Event Listener)来完成的。
-
常用事件监听器
-
事件的集中常见写法