000-Java 关键字
Java 的基本数据类型有 8 种,分别是:
- 整数型(4种):byte、short、int、long、
- 浮点型(2种):float、double
- 字符类型(1种):char
- 布尔类型(1种):boolean
引用数据类型(3种)。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时候被指定为一个特定的类型,声明之后不能改变。
- 引用数据类型包括:类(对象)、接口、数组。
- 所有引用类型默认值都是 null.
- 一个引用变量可以用于引用任何与之兼容的类型。
关键字
abstract 抽象
public 公共
private 私有
void 无参数返回
static 被类的所有对象共享
transient 序列化时不序列化此值
4.3 this关键字【应用】
- this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
1 |
|
this & super
this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
this&super关键字:
- this:代表本类对象的引用
- super:代表父类存储空间的标识(可以理解为父类对象引用)
this和super的使用分别
- 成员变量:
- this.成员变量 - 访问本类成员变量
- super.成员变量 - 访问父类成员变量
- 成员方法:
- this.成员方法 - 访问本类成员方法
- super.成员方法 - 访问父类成员方法
- 构造方法:
- this(…) - 访问本类构造方法
- super(…) - 访问父类构造方法
- 成员变量:
构造方法的注意事项
构造方法的创建
如果没有定义构造方法,系统将给出一个默认的无参数构造方法
如果定义了构造方法,系统将不再提供默认的构造方法构造方法的重载
如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法
推荐的使用方式
无论是否使用,都手工书写无参数构造方法
重要功能!
可以使用带参构造,为成员变量进行初始化
示例代码
1 |
|
1.多态
1.1多态的概述(记忆)
什么是多态
同一个对象,在不同时刻表现出来的不同形态
多态的前提
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
1.2多态中的成员访问特点(记忆)
成员访问特点
成员变量:编译看父类,运行看父类
成员方法:编译看父类,运行看子类
为什么成员变量和成员方法的访问不一样呢?
因为成员方法有重写,成员变量没有
代码演示
动物类
1
2
3
4
5
6
7public class Animal {
public int age = 40;
public void eat() {
System.out.println("动物吃东西");
}
}猫类
1
2
3
4
5
6
7
8
9
10
11
12
13public class Cat extends Animal {
public int age = 20;
public int weight = 10;
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("猫捉迷藏");
}
}测试类
1
2
3
4
5
6
7
8
9
10
11
12public class AnimalDemo {
public static void main(String[] args) {
//有父类引用指向子类对象
Animal a = new Cat();
System.out.println(a.age); // 40,父类的
// System.out.println(a.weight); // 报错
a.eat(); // 猫吃鱼,子类的
// a.playGame(); // 报错
}
}
1.3多态的好处和弊端(记忆)
好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
弊端
不能使用子类的特有成员和方法
1.4多态中的转型(应用)
向上转型
父类引用指向子类对象就是向上转型
向下转型
格式:子类型 对象名 = (子类型)父类引用;
代码演示
- 动物类
1
2
3
4
5public class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}- 猫类
1
2
3
4
5
6
7
8
9
10public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void playGame() {
System.out.println("猫捉迷藏");
}
}- 测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class AnimalDemo {
public static void main(String[] args) {
//多态
//向上转型
Animal a = new Cat();
a.eat();
// a.playGame();
//向下转型
Cat c = (Cat)a;
c.eat();
c.playGame();
}
}
2.抽象类
2.1抽象类的概述(理解)
当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!
2.2抽象类的特点(记忆)
抽象类和抽象方法必须使用 abstract 关键字修饰
1
2
3
4
5//抽象类的定义
public abstract class 类名 {}
//抽象方法的定义
public abstract void eat();抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化
抽象类如何实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
2.3抽象类的成员特点(记忆)
成员的特点
- 成员变量
- 既可以是变量
- 也可以是常量
- 构造方法,但是不能实例化,用于子类访问父类数据初始化
- 空参构造
- 有参构造
- 成员方法
- 抽象方法:限定子类必须实现
- 普通方法:提高代码复用性
- 成员变量
代码演示
- 动物类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public abstract class Animal {
private int age = 20;
private final String city = "北京";
public Animal() {}
public Animal(int age) {
this.age = age;
}
public void show() {
age = 40;
System.out.println(age);
// city = "上海";
System.out.println(city);
}
public abstract void eat();
}- 猫类
1
2
3
4
5
6public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}- 测试类
1
2
3
4
5
6
7public class AnimalDemo {
public static void main(String[] args) {
Animal a = new Cat();
a.eat();
a.show();
}
}
3.接口
3.1接口的概述(理解)
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
Java中的接口更多的体现在对行为的抽象!
3.2接口的特点(记忆)
接口用关键字interface修饰
1
public interface 接口名 {}
类实现接口用implements表示
1
public class 类名 implements 接口名 {}
接口不能实例化
接口如何实例化呢?参照多态的方式,通过实现类对象实例化,这叫接口多态。
多态的形式:具体类多态,抽象类多态,接口多态。
接口的子类
要么重写接口中的所有抽象方法
要么子类也是抽象类
3.3接口的成员特点(记忆)
成员特点
成员变量
只能是常量
默认修饰符:public static final构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解
代码演示
- 接口
1
2
3
4
5
6
7
8
9
10
11
12
13public interface Inter {
public int num = 10;
public final int num2 = 20;
// public static final int num3 = 30;
int num3 = 30;
// public Inter() {}
// public void show() {}
public abstract void method();
void show();
}- 实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 默认继承自 object 类
public class InterImpl extends Object implements Inter {
public InterImpl() {
super();
}
@Override
public void method() {
System.out.println("method");
}
@Override
public void show() {
System.out.println("show");
}
}- 测试类
1
2
3
4
5
6
7
8
9
10public class InterfaceDemo {
public static void main(String[] args) {
Inter i = new InterImpl();
// i.num = 20;
System.out.println(i.num);
// i.num2 = 40;
System.out.println(i.num2);
System.out.println(Inter.num);
}
}
3.5类和接口的关系(记忆)
类与类的关系
继承关系,只能单继承,但是可以多层继承
类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口与接口的关系
继承关系,可以单继承,也可以多继承
3.6抽象类和接口的区别(记忆)
成员区别
抽象类:变量,常量;有构造方法;有抽象方法,也有非抽象方法
接口:常量;抽象方法
关系区别
类与类:继承,单继承
类与接口:实现,可以单实现,也可以多实现
接口与接口:继承,单继承,多继承
设计理念区别
抽象类:对类抽象,包括属性、行为
接口:对行为抽象,主要是行为
1.Lambda表达式
1.1体验Lambda表达式【理解】
案例需求
启动一个线程,在控制台输出一句话:多线程程序启动了
实现方式一
- 实现步骤
- 定义一个类MyRunnable实现Runnable接口,重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable的对象作为构造参数传递
- 启动线程
- 实现步骤
实现方式二
- 匿名内部类的方式改进
实现方式三
- Lambda表达式的方式改进
代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30//方式一的线程类
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("多线程程序启动了");
}
}
public class LambdaDemo {
public static void main(String[] args) {
//方式一
// MyRunnable my = new MyRunnable();
// Thread t = new Thread(my);
// t.start();
//方式二
// new Thread(new Runnable() {
// @Override
// public void run() {
// System.out.println("多线程程序启动了");
// }
// }).start();
//方式三
new Thread( () -> {
System.out.println("多线程程序启动了");
} ).start();
}
}函数式编程思想概述
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”
而我们要学习的Lambda表达式就是函数式思想的体现
1.2Lambda表达式的标准格式【理解】
格式:
(形式参数) -> {代码块}
形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
->:由英文中画线和大于符号组成,固定写法。代表指向动作
代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
组成Lambda表达式的三要素:
- 形式参数,箭头,代码块
1.3Lambda表达式练习1【应用】
Lambda表达式的使用前提
有一个接口
接口中有且仅有一个抽象方法
练习描述
无参无返回值抽象方法的练习
操作步骤
定义一个接口(Eatable),里面定义一个抽象方法:void eat();
定义一个测试类(EatableDemo),在测试类中提供两个方法
一个方法是:useEatable(Eatable e)
一个方法是主方法,在主方法中调用useEatable方法
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36//接口
public interface Eatable {
void eat();
}
//实现类
public class EatableImpl implements Eatable {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
}
//测试类
public class EatableDemo {
public static void main(String[] args) {
//在主方法中调用useEatable方法
Eatable e = new EatableImpl();
useEatable(e);
//匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
});
//Lambda表达式
useEatable(() -> {
System.out.println("一天一苹果,医生远离我");
});
}
private static void useEatable(Eatable e) {
e.eat();
}
}
1.4Lambda表达式练习2【应用】
练习描述
有参无返回值抽象方法的练习
操作步骤
定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
定义一个测试类(FlyableDemo),在测试类中提供两个方法
一个方法是:useFlyable(Flyable f)
一个方法是主方法,在主方法中调用useFlyable方法
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public interface Flyable {
void fly(String s);
}
public class FlyableDemo {
public static void main(String[] args) {
//在主方法中调用useFlyable方法
//匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s);
System.out.println("飞机自驾游");
}
});
System.out.println("--------");
//Lambda
useFlyable((String s) -> {
System.out.println(s);
System.out.println("飞机自驾游");
});
}
private static void useFlyable(Flyable f) {
f.fly("风和日丽,晴空万里");
}
}
1.5Lambda表达式练习3【应用】
练习描述
有参有返回值抽象方法的练习
操作步骤
定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
定义一个测试类(AddableDemo),在测试类中提供两个方法
一个方法是:useAddable(Addable a)
一个方法是主方法,在主方法中调用useAddable方法
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public interface Addable {
int add(int x,int y);
}
public class AddableDemo {
public static void main(String[] args) {
//在主方法中调用useAddable方法
useAddable((int x,int y) -> {
return x + y;
});
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
1.6Lambda表达式的省略模式【应用】
省略的规则
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return关键字也要省略
代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42public interface Addable {
int add(int x, int y);
}
public interface Flyable {
void fly(String s);
}
public class LambdaDemo {
public static void main(String[] args) {
// useAddable((int x,int y) -> {
// return x + y;
// });
//参数的类型可以省略
useAddable((x, y) -> {
return x + y;
});
// useFlyable((String s) -> {
// System.out.println(s);
// });
//如果参数有且仅有一个,那么小括号可以省略
// useFlyable(s -> {
// System.out.println(s);
// });
//如果代码块的语句只有一条,可以省略大括号和分号
useFlyable(s -> System.out.println(s));
//如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉
useAddable((x, y) -> x + y);
}
private static void useFlyable(Flyable f) {
f.fly("风和日丽,晴空万里");
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
1.7Lambda表达式的注意事项【理解】
使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
必须有上下文环境,才能推导出Lambda对应的接口
根据局部变量的赋值得知Lambda对应的接口
Runnable r = () -> System.out.println(“Lambda表达式”);根据调用方法的参数得知Lambda对应的接口
new Thread(() -> System.out.println(“Lambda表达式”)).start();
1.8Lambda表达式和匿名内部类的区别【理解】
所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
2.接口组成更新
2.1接口组成更新概述【理解】
常量
public static final抽象方法
public abstract默认方法(Java 8)
静态方法(Java 8)
私有方法(Java 9)
2.2接口中默认方法【应用】
格式
public default 返回值类型 方法名(参数列表) { }
范例
1
2public default void show3() {
}注意事项
默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
public可以省略,default不能省略
2.3接口中静态方法【应用】
格式
public static 返回值类型 方法名(参数列表) { }
范例
1
2public static void show() {
}注意事项
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public可以省略,static不能省略
2.4接口中私有方法【应用】
私有方法产生原因
Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
定义格式
格式1
private 返回值类型 方法名(参数列表) { }
范例1
1
2private void show() {
}格式2
private static 返回值类型 方法名(参数列表) { }
范例2
1
2private static void method() {
}
注意事项
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
3.方法引用
3.1体验方法引用【理解】
方法引用的出现原因
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要
那我们又是如何使用已经存在的方案的呢?
这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案
代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public interface Printable {
void printString(String s);
}
public class PrintableDemo {
public static void main(String[] args) {
//在主方法中调用usePrintable方法
// usePrintable((String s) -> {
// System.out.println(s);
// });
//Lambda简化写法
usePrintable(s -> System.out.println(s));
//方法引用
usePrintable(System.out::println);
}
private static void usePrintable(Printable p) {
p.printString("爱生活爱Java");
}
}
3.2方法引用符【理解】
方法引用符
:: 该符号为引用运算符,而它所在的表达式被称为方法引用推导与省略
- 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
3.3引用类方法【应用】
引用类方法,其实就是引用类的静态方法
格式
类名::静态方法
范例
Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
练习描述
定义一个接口(Converter),里面定义一个抽象方法 int convert(String s);
定义一个测试类(ConverterDemo),在测试类中提供两个方法
一个方法是:useConverter(Converter c)
一个方法是主方法,在主方法中调用useConverter方法
代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public interface Converter {
int convert(String s);
}
public class ConverterDemo {
public static void main(String[] args) {
//Lambda写法
useConverter(s -> Integer.parseInt(s));
//引用类方法
useConverter(Integer::parseInt);
}
private static void useConverter(Converter c) {
int number = c.convert("666");
System.out.println(number);
}
}使用说明
Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
3.4引用对象的实例方法【应用】
引用对象的实例方法,其实就引用类中的成员方法
格式
对象::成员方法
范例
“HelloWorld”::toUpperCase
String类中的方法:public String toUpperCase() 将此String所有字符转换为大写
练习描述
定义一个类(PrintString),里面定义一个方法
public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出
定义一个接口(Printer),里面定义一个抽象方法
void printUpperCase(String s)
定义一个测试类(PrinterDemo),在测试类中提供两个方法
- 一个方法是:usePrinter(Printer p)
- 一个方法是主方法,在主方法中调用usePrinter方法
代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class PrintString {
//把字符串参数变成大写的数据,然后在控制台输出
public void printUpper(String s) {
String result = s.toUpperCase();
System.out.println(result);
}
}
public interface Printer {
void printUpperCase(String s);
}
public class PrinterDemo {
public static void main(String[] args) {
//Lambda简化写法
usePrinter(s -> System.out.println(s.toUpperCase()));
//引用对象的实例方法
PrintString ps = new PrintString();
usePrinter(ps::printUpper);
}
private static void usePrinter(Printer p) {
p.printUpperCase("HelloWorld");
}
}使用说明
Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数
3.5引用类的实例方法【应用】
引用类的实例方法,其实就是引用类中的成员方法
格式
类名::成员方法
范例
String::substring
public String substring(int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex
练习描述
定义一个接口(MyString),里面定义一个抽象方法:
String mySubString(String s,int x,int y);
定义一个测试类(MyStringDemo),在测试类中提供两个方法
一个方法是:useMyString(MyString my)
一个方法是主方法,在主方法中调用useMyString方法
代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public interface MyString {
String mySubString(String s,int x,int y);
}
public class MyStringDemo {
public static void main(String[] args) {
//Lambda简化写法
useMyString((s,x,y) -> s.substring(x,y));
//引用类的实例方法
useMyString(String::substring);
}
private static void useMyString(MyString my) {
String s = my.mySubString("HelloWorld", 2, 5);
System.out.println(s);
}
}使用说明
Lambda表达式被类的实例方法替代的时候 第一个参数作为调用者 后面的参数全部传递给该方法作为参数
3.6引用构造器【应用】
引用构造器,其实就是引用构造方法
格式
类名::new
范例
Student::new
练习描述
定义一个类(Student),里面有两个成员变量(name,age)
并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法
定义一个接口(StudentBuilder),里面定义一个抽象方法
Student build(String name,int age);
定义一个测试类(StudentDemo),在测试类中提供两个方法
一个方法是:useStudentBuilder(StudentBuilder s)
一个方法是主方法,在主方法中调用useStudentBuilder方法
代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public interface StudentBuilder {
Student build(String name,int age);
}
public class StudentDemo {
public static void main(String[] args) {
//Lambda简化写法
useStudentBuilder((name,age) -> new Student(name,age));
//引用构造器
useStudentBuilder(Student::new);
}
private static void useStudentBuilder(StudentBuilder sb) {
Student s = sb.build("林青霞", 30);
System.out.println(s.getName() + "," + s.getAge());
}
}使用说明
Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数