一、JDK8 和 JDK9 接口新特性
1. JDK8 接口新特性
a. 允许在接口中定义非抽象方法,但是需要使用关键字 default 修饰,这些方法就是默认方法
作用: 解决接口升级的问题;需要在接口中添加新的方法时可以不用修改所有的实现类。
接口中默认方法的定义格式:
- 格式:public default 返回值类型 方法名(参数列表) { }
- 范例:public default void show( ) { }
注意事项:
- 默认方法不是抽象方法,所以不强制被重写(但是可以被重写,重写的时候去掉 default 关键字)
- public 可以省略,default 不能省略
- 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
b. 允许在接口中定义 static 静态方法
接口中静态方法的定义格式:
- 格式:public static 返回值类型 方法名(参数列表) { }
- 范例:public static void show( ) { }
注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public 可以省略,static 不能省略
2. JDK9 接口新特性
接口中允许定义 private 私有方法
接口中私有方法的定义格式:
- 格式1:private 返回值类型 方法名(参数列表) { }
- 范例1:private void show( ) { }
- 格式2:private static 返回值类型 方法名(参数列表) { }
- 范例2:private static void method( ) { }
例如:
interface Inter {
public default void start() {
System.out.println("start...");
System.out.println("日志记录");
}
public default void end() {
System.out.println("end...");
System.out.println("日志记录");
}
}
该接口的两个默认方法中存在逻辑相同的代码,可以将其抽取出来作为接口的私有方法:
interface Inter {
public default void start() {
System.out.println("start...");
log();
}
public default void end() {
System.out.println("end...");
log();
}
private void log() {
System.out.println("日志记录");
}
}
作用:提升复用性,减少冗余代码
二、代码块
使用 { } 括起来的代码被称为代码块
- 局部代码块
位置:方法中定义
作用:限定变量的生命周期,及早释放,提高内存利用率 - 构造代码块
位置:类中方法外定义
特点:每次构造方法执行的时候,都会执行该代码块中的代码,并且在构造方法执行前执行
作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性 - 静态代码块
位置:类中方法外定义
特点:需要通过 static 关键字修饰,随着类的加载而加载,并且只执行一次
作用:在类加载的时候做一些数据初始化的操作 - 同步代码块
三、内部类
1. 成员内部类
成员内部类就是定义在一个类里面的类:
class Outer {
//内部类
class Inner {
}
}
创建对象的格式:
- 格式:外部类名.内部类名 对象名 = new 外部类对象( ).new 内部类对象( );
- 范例:Outer.Inner oi = new Outer( ).new Inner( );
内部类成员访问:
- 内部类中,访问外部类成员:直接访问,包括私有类型成员
- 外部类中,访问内部类成员:需要先创建内部类对象再访问
例如:
public class Outer {
Inner i = new Inner();
private void method() {//私有类型方法内部类仍可以直接访问
System.out.println("method...");
System.out.println(i.num);//外部类中访问内部类成员--->先创建对象再访问
}
class Inner {
int num = 10;
public void show() {
System.out.println("show...");
method();//内部类中访问外部类成员--->直接访问
}
}
}
测试类:
public class InnerDemo {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
System.out.println(oi.num);
oi.show();
}
}
运行结果为:
10
show...
method...
10
注意:在成员内部类中访问所在外部类对象,格式:外部类名.this
class Outer {
int num = 10;
class Inner {
int num = 20;
public void show() {
int num = 30;
System.out.println(num); //30
System.out.println(this.num); //20
System.out.println(Outer.this.num); //10
}
}
}
2. 静态内部类
有 static 修饰的成员内部类:
class Outer {
static class Inner {
}
}
静态内部类创建对象的格式:
- 格式:外部类名.内部类名 对象名 = new 外部类名.内部类对象( );
- 范例:Outer.Inner oi = new Outer.Inner( );
例如:
class OuterClass {
static class InnerClass {
public void show() {
System.out.println("show...");
}
}
}
测试类:
public class StaticInnerDemo {
public static void main(String[] args) {
OuterClass.InnerClass oi = new OuterClass.InnerClass();
oi.show();//运行结果为show...
}
}
如果 show 方法是静态方法,则可以通过类名一级一级调用:
public class StaticInnerDemo {
public static void main(String[] args) {
OuterClass.InnerClass.show();
}
}
class OuterClass {
static class InnerClass {
public static void show() {
System.out.println("show...");
}
}
}
注意:静态只能访问静态
3. 局部内部类
放在方法、代码块、构造器等执行体中,用处不是很大,在这边就不再叙述了。
4. 匿名内部类
概述: 匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)。
前提: 需要存在一个接口或类
格式:
- new 类名( ) { }:代表继承这个类
- new 接口名( ) { }:代表实现这个接口
作用: 匿名内部类可以使代码更加简洁,定义一个类的同时对其进行实例化。
使用总结: 匿名内部类可以作为方法的实际参数进行传输。
如果一个方法的形参是接口类型,应该传入的是该接口的实现类对象:
- 若该接口的抽象方法只有1个或很少,则可以通过匿名内部类来作为参数进行传输:
public class AnonClassDemo {
public static void main(String[] args) {
//通过匿名内部类进行参数传输
useInter(new Inter() {
@Override
public void show() {
System.out.println("show...");
}
});
}
//方法的形参是接口类型
public static void useInter(Inter i) {
i.show();
}
}
//接口的抽象方法只有1个
interface Inter {
void show();
}
- 若该接口的抽象方法较多,则可以通过实现类来进行参数传递:
public class AnonClassDemo {
public static void main(String[] args) {
useInter(new InterImpl());//相当于 Inter i = new InterImpl();
}
public static void useInter(Inter i) {
i.show1();
i.show2();
i.show3();
}
}
interface Inter {
void show1();
void show2();
void show3();
}
//实现类重写所有的抽象方法
class InterImpl implements Inter {
@Override
public void show1() {
System.out.println("show1...");
}
@Override
public void show2() {
System.out.println("show2...");
}
@Override
public void show3() {
System.out.println("show3...");
}
}
四、Lambda 表达式
1. Lambda 概述
- Lambda 表达式是 JDK8 开始后的一种新语法形式
- 作用:简化匿名内部类的代码写法
- Lambda 表达式的简化格式:
()-> { }
(匿名内部类被重写方法的形参列表)-> {被重写方法的方法体代码} - Lambda 表达式只能简化函数式接口的匿名内部类的写法形式
- 什么是函数式接口?
首先必须是接口、其次接口中有且仅有一个抽象方法的形式
通常我们会在接口上加上一个@FunctionalInterface 注解,标记该接口必须是满足函数式接口
以上述匿名内部类的代码为例进行 Lambda 表达式的简化:
public class LambdaDemo01 {
public static void main(String[] args) {
useInter(() ->{
System.out.println("show...");
});
}
public static void useInter(Inter i) {
i.show();
}
}
interface Inter {
void show();
}
2. Lambda 表达式的省略写法
- 参数类型可以省略不写。
- 如果只有一个参数,参数类型可以省略,同时()也可以省略。
- 如果 Lambda 表达式的方法体代码只有一行代码
可以省略大括号不写,同时要省略分号
此时,如果这行代码是 return 语句,必须省略 return 不写,同时也必须省略 " ; "不写
3. Lambda 表达式和匿名内部类的区别
使用限制不同
- 匿名内部类:可以操作类,接口
- Lambda 表达式:只能操作函数式接口
实现原理不同
- 匿名内部类:编译之后,产生一个单独的 .class 字节码文件
- Lambda 表达式:编译之后,没有一个单独的 .class 字节码文件
五、窗体、组件、事件
1. 窗体对象 JFrame
import javax.swing.*;
public class FrameTest {
public static void main(String[] args) {
//创建窗体对象
JFrame frame = new JFrame();
//设置宽和高
frame.setSize(514,595);
//设置关闭模式
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//设置窗体标题
frame.setTitle("窗体");
//设置窗体可见,一般位于代码的最后
frame.setVisible(true);
}
}
2. 按钮组件 JButton
import javax.swing.*;
/*
JButton构造方法:
1. public JButton():创建一个空白的按钮
2. public JButton(String text):创建一个带文本的按钮
注意:窗体默认给组件布局设置为充满窗体,如果取消了窗体的默认布局,就需要手动指定组件的摆放位置
*/
public class ButtonTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500,500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//取消窗体默认布局
frame.setLayout(null);
//1. 创建按钮对象
JButton btn = new JButton("点我呀~");
//2. 手动指定组件摆放位置
btn.setBounds(50,50,100,100);
//3. 将按钮添加到窗体的 [面板对象] 中
frame.getContentPane().add(btn);
frame.setVisible(true);
}
}
3. JLabel 组件展示文本和图片
import javax.swing.*;
/*
JLabel构造方法:
1. JLabel(String text) 使用指定的文本创建一个 JLabel 对象
2. JLabel(Icon image) 创建一个具有指定图像的 JLabel 对象
*/
public class LabelTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(800,500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLayout(null);
//创建一个 JLabel 对象用于展示文本
JLabel jl1 = new JLabel("你好!");
jl1.setBounds(50,50,100,100);
frame.getContentPane().add(jl1);
创建一个 JLabel 对象用于展示图片
JLabel jl2 = new JLabel(new ImageIcon("E:\\image\\1.jpeg"));//图片路径
jl2.setBounds(150,50,500,281);
frame.getContentPane().add(jl2);
frame.setVisible(true);
}
}
4. 事件监听
动作监听器:ActionListener
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/*
动作事件:ActionListener
通过 1.鼠标点击 2.空格按键 触发
*/
public class ActionListenerTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500,500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLayout(null);
//创建一个按钮组件
JButton btn = new JButton("按钮");
btn.setBounds(0,0,100,100);
frame.getContentPane().add(btn);
/*
给该按钮组件创建一个动作事件
该添加方法的形参是一个接口,所以使用匿名内部类来进行参数传递
*/
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("我被点了!!!");
}
});
frame.setVisible(true);
}
}
键盘监听器:KeyListener
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/*
键盘事件:KeyListener
敲击键盘任意键位触发
*/
public class KeyListenerTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500,500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.addKeyListener(new KeyListener() {
//只能监听到键盘的一部分按键,该方法不常用
@Override
public void keyTyped(KeyEvent e) {
}
//按下某个键时调用此方法
@Override
public void keyPressed(KeyEvent e) {
//键盘的每个键位代表着不同的数字,可以通过数字判断来满足不同需求
int KeyCode = e.getKeyCode();
if (KeyCode == 37) {
System.out.println("左移动业务代码");
}
else if (KeyCode == 38) {
System.out.println("上移动业务代码");
}
else if (KeyCode == 39) {
System.out.println("右移动业务代码");
}
else {
System.out.println("下移动业务代码");
}
}
//释放某个键时调用此方法
@Override
public void keyReleased(KeyEvent e) {
}
});
frame.setVisible(true);
}
}
事件冲突
焦点:程序的注意力集中在了某一个组件上
import javax.swing.*;
public class Tips {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500,500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLayout(null);
//按钮组件自带焦点
JButton btn = new JButton("按钮");
btn.setBounds(0,0,100,100);
frame.getContentPane().add(btn);
//取消焦点
btn.setFocusable(false);
frame.setVisible(true);
}
}
六、设计模式
设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
1. 适配器设计模式
- 解决接口与接口实现类之间的矛盾问题
- 实现步骤:
1、编写一个 xxxAdapter 类,实现对应接口
2、重写内部所有抽象方法,但方法都是空实现
3、让自己的类去继承适配器类,重写自己需要的方法即可
4、为了避免其他类创建适配器类的对象,使用 abstract 进行修饰
例如:
public class Test {
public static void main(String[] args) {
useAnimal u = new useAnimal();
u.eat();
u.drink();
}
}
interface Animal {
void eat();
void drink();
void sleep();
void play();
}
//适配器类实现接口
abstract class AnimalAdapter implements Animal {
@Override
public void eat() {
}
@Override
public void drink() {
}
@Override
public void sleep() {
}
@Override
public void play() {
}
}
//重写自己需要的方法
class useAnimal extends AnimalAdapter {
@Override
public void eat() {
System.out.println("eat...");
}
@Override
public void drink() {
System.out.println("drink...");
}
}
2. 模板设计模式
- 模板设计模式:把抽象类整体就可以看作成一个模板,模板中不能决定的东西定义成抽象方法,让使用模板的类(基础抽象类的类)去重写抽象方法实现需求
- 小结:模板设计模式的优势,模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可