接口和多态-静态修饰符-Objects-Arrays-匿名内部类

接口和多态-静态修饰符-Objects-Arrays-匿名内部类
Smith1.接口
1.1 概述
- 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
- Java中接口存在的两个意义
- 用来定义规范
- 用来做功能的拓展
//接口和抽象类都不能创建对象
1.2接口的特点
1 | public interface 接口名 { |
1 | public class 类名 implements 接口名 {} |
接口不能实例化
我们可以创建接口的实现类对象使用
接口的子类
要么重写接口中的所有抽象方法
要么子类也是抽象类
1.3接口的成员特点(记忆)
成员特点
成员变量
只能是常量
默认修饰符:public static final构造方法
没有,因为接口主要是扩展功能的,而没有具体存在成员方法
只能是抽象方法
默认修饰符:public abstract
代码演示
- 接口
1
2
3
4
5public interface Inter {
public static final int NUM = 10;
public abstract void show();
}- 实现类
1
2
3
4
5
6
7
8
9
10
11class InterImpl implements Inter{
public void method(){
// NUM = 20;
System.out.println(NUM);
}
public void show(){
}
}- 测试类
1
2
3
4
5public class TestInterface {
public static void main(String[] args) {
System.out.println(Inter.NUM);
}
}
1.4类和接口的关系(记忆)
类与类的关系
继承关系,只能单继承,但是可以多层继承
类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口与接口的关系
继承关系,可以单继承,也可以多继承
我们看一个案例演示,假设有一个Studnet学生类,还有一个Driver司机的接口,还有一个Singer歌手的接口。
现在要写一个A类,想让他既是学生,偶然也是司机能够开车,偶尔也是歌手能够唱歌。那我们代码就可以这样设计,如下:
1 | class Student{ |
2.接口新概念
2.1概述
常量
public static final
抽象方法
public abstract
默认方法(Java 8)
静态方法(Java 8)
私有方法(Java 9)
2.2接口中默认方法【应用】
作用 解决接口升级的问题
1
2public default void show3() {
}默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
public可以省略,default不能省略
如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
2.3接口中静态方法
1 | public static void show() { |
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
1 | public interface A { //接口中调用方法按使用默认default |
2.4接口中私有方法
私有方法产生原因
当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
1
2private void show() {
}1
2private static void method() {
}- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
2.5接口的案例
首先我们写一个学生类,用来描述学生的相关信息
1 | public class Student { |
接着,写一个StudentOperator接口,表示学生信息管理系统的两个功能。
1 | public interface StudentOperator { |
然后,写一个StudentOperator接口的实现类StudentOperatorImpl1,采用第1套方案对业务进行实现。
1 | public class StudentOperatorImpl1 implements StudentOperator{ |
接着,再写一个StudentOperator接口的实现类StudentOperatorImpl2,采用第2套方案对业务进行实现。
1 | public class StudentOperatorImpl2 implements StudentOperator{ |
再写一个班级管理类ClassManager,在班级管理类中使用StudentOperator的实现类StudentOperatorImpl1对学生进行操作
1 | public class ClassManager { |
最后,再写一个测试类Test,在测试类中使用ClassMananger完成班级学生信息的管理。
1 | public class Test { |
2.6接口的其他细节
- 一个接口可以继承多个接口
1 | interface A{ |
接口除了上面的多继承特点之外,在多实现、继承和实现并存时,有可能出现方法名冲突的问题
1 | 1.一个接口继承多个接口,如果多个接口中存在`相同的方法声明`,则此时不支持多继承 |
综上所述:一个接口可以继承多个接口,接口同时也可以被类实现。
3.多态
3.1多态的概述
什么是多态
表现为:对象多态、行为多态
同一个对象,在不同时刻表现出来的不同形态多态的前提
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
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
29class Animal {
public void eat(){
System.out.println("动物吃饭");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Test1Polymorphic {
/*
多态的前提:
1. 要有(继承 \ 实现)关系
2. 要有方法重写
3. 要有父类引用, 指向子类对象
*/
public static void main(String[] args) {
// 当前事物, 是一只猫
Cat c = new Cat();
// 当前事物, 是一只动物
Animal a = new Cat();
a.eat();
}
}
3.2多态中的成员访问特点(记忆)
成员访问特点
成员变量 编译看父类,运行看父类
成员方法 编译看父类,运行看子类
//成员变量
编译看左边有没有成员变量
运行也看左边有没有成员变量//成员方法
编译看左边有没有方法
运行看右边有没有方法代码演示
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
28class Fu {
int num = 10;
public void method(){
System.out.println("Fu.. method");
}
}
class Zi extends Fu {
int num = 20;
public void method(){
System.out.println("Zi.. method");
}
}
public class Test2Polymorpic {
/*
多态的成员访问特点:
成员变量: 编译看左边 (父类), 运行看左边 (父类)
成员方法: 编译看左边 (父类), 运行看右边 (子类)
*/
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num);
f.method();
}
}
3.3多态的好处和弊端
好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
弊端
不能使用子类的特有成员
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87public abstract class Animal {
private String name;
private int age;
...
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void eat();
}
public class Dog extends Animal{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
public void eat() {
System.out.println(getAge()+"岁的狗"+getName()+"正在吃狗粮...");
}
}
public class Pig extends Animal{
public Pig() {
}
public Pig(String name, int age) {
super(name, age);
}
public void eat() {
System.out.println(getAge()+"岁的猪"+getName()+"正在吃白菜...");
}
}
/*
1: 核心职责是 "接收"老板传递的动物,并
面向动物对象,指挥动物吃饭!
饲养员不能决定具体是什么动物
代码该如何设计,才能做到可以接收老板
传递的任意一个动物,并成功的调用动物
的吃饭方法???
为了能接收到任意类型的动物对象,可以
采用多态的技术,设计父类类型的参数!
*/
public class SiYangYuan {
// 多态的好处,就体现在了这里
public void siYang(Animal animal) {
// 利用多态的特点,面向多态的对象,调用 eat方法,编译的时候,检查 Animal,运行的时候,找正在的子类重写的 eat方法
System.out.println("饲养员准备好了动物的食物..................");
animal.eat();
}
}
/*
饲养场的老板:(测试类)
1: 负责决定饲养什么动物
2: 负责招聘1名员工,指挥员工饲养动物
*/
public class Boss {
public static void main(String[] args) {
//1: 负责决定饲养什么动物
Pig pig = new Pig("乔治",2);
// 2: 负责招聘1名员工,指挥员工饲养动物
SiYangYuan s = new SiYangYuan();
s.siYang(pig);
// 3: 老板决定添加一种新的动物,交给饲养员饲养
Dog dog = new Dog("旺财",3);
s.siYang(dog);
}
}
3.4多态中的转型
向上转型 父类引用指向子类对象就是向上转型
向下转型
格式:子类型 对象名 = (子类型)父类引用;
代码演示
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
33class Fu {
public void show(){
System.out.println("Fu..show...");
}
}
class Zi extends Fu {
public void show() {
System.out.println("Zi..show...");
}
public void method(){
System.out.println("我是子类特有的方法, method");
}
}
public class Test3Polymorpic {
public static void main(String[] args) {
// 1. 向上转型 : 父类引用指向子类对象
Fu f = new Zi();
f.show();
// 多态的弊端: 不能调用子类特有的成员
// f.method();
// A: 直接创建子类对象
// B: 向下转型
// 2. 向下转型 : 从父类类型, 转换回子类类型
Zi z = (Zi) f;
z.method();
}
}
3.5多态中转型存在的风险和解决方案 (应用)
风险
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
解决方案
关键字 instanceof
使用格式
变量名 instanceof 类型 通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
代码演示
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
42abstract class Animal {
public abstract void eat();
}
class Dog extends Animal {
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome(){
System.out.println("看家");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Test4Polymorpic {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
}
public static void useAnimal(Animal a){ // Animal a = new Dog();
// Animal a = new Cat();
a.eat();
//a.watchHome();
// Dog dog = (Dog) a;
// dog.watchHome(); // ClassCastException 类型转换异常
// 判断a变量记录的类型, 是否是Dog
if(a instanceof Dog){
Dog dog = (Dog) a;
dog.watchHome();
}
}
}
原本是什么类型,才能还原成什么类型
5. static关键字
5.1 static关键字的概念
用于修饰类的成员(变量、方法、代码块和内部类)
而是属于整个类,被所有对象共享
1 | static关键字: |
5.2 static修饰成员变量的使用
static修饰修饰成员变量(类变量: 依赖于类,与对象无关)
1 | 1.定义格式: |
Java中的成员变量按照有无static修饰分为两种:类变量、实例变量。
1 | - 1.类变量:属于类,在内存中只有一份,用类名调用 |
1 | public class Student { |
5.3 static修饰成员方法的使用
成员方法根据有无static也分为两类:类方法、实例方法
1 | static修饰成员方法(类方法: 依赖于类,与对象无关) |
- 先定义一个Student类,在类中定义一个类方法、定义一个实例方法
1 | public class Student{ |
- 在定义一个测试类,注意类方法、对象方法调用的区别
1 | public class Test2{ |
static修饰成员方法的内存原理
1 | 1.类方法:static修饰的方法,可以被类名调用,是因为它是随着类的加载而加载的; |
1 | public class JavaEE372Student { |
5.4 静态的注意事项
1 | 静态的内容只能使用静态的内容,不能使用非静态的内容 |
1 | //此类只针对智晟的老师 |
5.5 静态代码块
1 | 静态代码块 |
执行顺序
用一句话总结核心规律:先静态、后实例;先父类、后子类;先块、后构造。
下面用 “建造一栋楼” 的比喻来理解,再配上代码验证。
比喻:建造一栋楼
静态代码块= 打地基(只做一次,盖楼前必须先打好)构造方法= 装修房间(每次造一个新房间都要装修)super()= 先装修父楼层,再装修子楼层实例代码块= 每次装修前都要做的固定准备工作—
1 | class Animal { |
输出结果:
1 | === 第一次 new Dog() === |
三个最容易踩的坑
坑 1:静态块只执行一次 第二次 new Dog() 时,① 和 ② 不会再出现,因为类已经加载过了。
坑 2:子类构造方法第一行隐式是 super() 即使你不写 super(),Java 编译器也会自动加上,所以父类构造一定先跑。
坑 3:实例块在构造方法之前 { ... } 实例代码块会被编译器插入到每个构造方法的最前面(super() 之后),可以理解成”构造方法的前置准备”。
1 | public class Demo07StaticKuai { |
6. 内部类
6.1 内部类的概念
内部类是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类。
1 | 内部类概念: |
比如:汽车(外部类)内部有个发动机(内部类)
1 | public class Car{ |
内部类有四种形式,分别是 成员内部类、静态内部类、局部内部类、匿名内部类。
6.2静态内部类
在成员内部类的前面加了一个static关键字。静态内部类属于外部类自己持有。
1 | public class Outer { |
静态内部类创建对象时,需要使用外部类的类名调用。
1 | //格式:外部类.内部类 变量名 = new 外部类.内部类(); |
6.3局部内部类
局部内部类是定义在方法中的类,和局部变量一样,只能在方法中有效。所以局部内部类的局限性很强,一般在开发中是不会使用的。
1 | public class Outer{ |
6.2 匿名内部类的使用
我们还是先认识一下什么是匿名内部类?
匿名内部类是一种特殊的局部内部类;所谓匿名,指的是程序员不需要为这个类声明名字。
下面就是匿名内部类的格式:
1 | new 父类/接口(参数值){ |
匿名内部类本质上是一个没有名字的子类对象、或者接口的实现类对象。
1 | /* |
1 | //例1: |
7. Arrays工具类
一、什么是工具类?
工具类(Utility Class) 是Java编程中一种常见的设计模式,具有以下特点:
- 静态方法为主:主要提供静态方法,通过类名直接调用
- 不可实例化:通常将构造函数私有化,防止被实例化
- 功能专一:专注于某一领域的通用功能
- 无状态:不维护对象状态,所有方法都是无状态的
- 工具性质:提供通用、可重用的功能方法
如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,
由于调用起来非常方便,就像一个工具一下,所以把这样的类就叫做工具类。
- 我们写一个生成验证码的工具类
1 | public class MyUtils{ |
- 接着可以在任何位置调用
MyUtils的createCOde()方法产生任意个数的验证码
1 | //比如这是一个登录界面 |
工具类的使用就是这样子的,学会了吗?
在补充一点,工具类里的方法全都是静态的,推荐用类名调用为了防止使用者用对象调用。我们可以把工具类的构造方法私有化。
1 | public class MyUtils{ |
二、Arrays工具类的作用
java.util.Arrays 是Java集合框架的一部分,专门用于操作数组的工具类。
它提供了一系列静态方法,用于:
- 数组排序:对数组元素进行排序
- 数组搜索:在数组中查找特定元素
- 数组比较:比较两个数组是否相等
- 数组填充:用指定值填充数组
- 数组转换:将数组转换为字符串或其他形式
- 数组复制:复制数组的部分或全部内容
- 数组流操作:将数组转换为流进行函数式编程
三、Arrays工具类的常见方法
| 方法类别 | 方法名 | 描述 | 主要参数 | 返回值 | 注意事项 |
|---|---|---|---|---|---|
| 二分查找 | binarySearch() |
在有序数组中查找指定元素 | 数组, 要查找的值 | 元素索引(找到时)或负数(未找到时) | 1. 数组必须已排序 2. 可以指定搜索范围 3. 未找到时返回负数表示插入点 |
| 数组转字符串 | toString() |
将一维数组转换为字符串 | 数组 | 数组的字符串表示 | 1. 用于基本类型和对象数组 2. 格式为 [元素1, 元素2, ...] |
| 数组排序 | sort() |
对数组进行排序 | 数组 | 无(原地排序) | 1. 默认自然顺序(升序) 2. 可以指定排序范围 3. 对象数组需要Comparable |
| 数组复制 | copyOf() |
复制数组 | 原数组, 新长度 | 新数组 | 1. 新长度可大于或小于原数组 2. 超长部分用默认值填充 |
| 数组复制 | copyOfRange() |
复制数组指定范围 | 原数组, 起始索引, 结束索引 | 新数组 | 1. 包含起始索引 2. 不包含结束索引 3. 可扩展范围 |
1 | // 1: 准备数组 |
8. Objects工具类
1 | /* |














