目录
面向对象三大特征之三:多态
多态的概述、形式
什么是多态?
同类型的对象,执行同一行为,会表现出不同的行为特征
多态的常见形式
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
//抽象父类
public abstract class Animal {
public abstract void run();
}
//狗类
public class Dog extends Animal {
@Override
public void run() {
System.out.println("狗跑的贼快~~~");
}
}
//乌龟类
public class Tortoise extends Animal {
@Override
public void run() {
System.out.println("乌龟跑得很慢~~~");
}
}
//测试类
//认识多态,理解多态的形式和概念
public class Test {
public static void main(String[] args) {
//1、多态的形式 父类类型 对象名称 = new 子类构造器;
Animal a = new Dog();
a.run();//狗跑的贼快~~~
Animal a2 = new Tortoise();
a2.run();//乌龟跑得很慢~~~
}
}
多态成员访问特点
方法调用:编译看左,运行看右
变量调用:编译看左,运行也看左(多态侧重行为多态)
多态的前提
有继承/实现关系;有父类引用指向子类对象;有方法重写。
多态的优势
优势
在多态形式下,右边对象可以实现解耦合,便于扩展和维护
定义方法的时候,使用父类型作为参数,该方法就可以接受这父类的一切子类对象,体现出多态的扩展性与便利
父类作为参数传递
多态下会产生的一个问题
多态下不能使用子类独有功能
多态下引用数据类型的类型转换
自动类型转换(从子到父):子类对象赋值给父类类型的变量指向
强制类型转换(从父到子)
此时必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量
作用:可以解决多态下的劣势,可以实现调用子类独有的功能
注意:如果转型后的数据和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
Java建议强转转换前使用instanceof判断当前对象的真是类型,再进行强制转换
变量名 instanceof 真实类型
判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,是则返回true,反之。
Animal t = new Tortoise();
Dog d = (Dog)t//出现异常ClassCastException
//学习多态形式下的类转换机制
public class Test {
public static void main(String[] args) {
//自动类型转换
Animal a = new Dog();
a.run();
//强制类型转换:
Animal a2 = new Tortoise();
a2.run();
//Dog d = (Dog)a2;//强制类型转换,编译阶段不报错(注意:有继承或实现关系编译阶段可以强制,运行时可能报错!
if (a2 instanceof Tortoise) {
Tortoise t = (Tortoise) a2;//从父类到子类的强制转换
t.layEggs();
} else if (a2 instanceof Dog) {
Dog d = (Dog) a2;
d.lookDoor();
}
System.out.println("-----------------");
go(new Dog());
go(new Tortoise());
}
public static void go(Animal a) {
a.run();
//a到底是乌龟还是狗?
if (a instanceof Tortoise) {
Tortoise t = (Tortoise) a;//从父类到子类的强制转换
t.layEggs();
} else if (a instanceof Dog) {
Dog d = (Dog) a;
d.lookDoor();
}
}
}
多态的案例
需求:
使用面向对象编程模拟:设计一个电脑对象,可以安装2个USB设备
鼠标:被安装是可以完成接入、调用点击功能、拔出功能
键盘:被安装是可以完成接入、调用打字功能、拔出功能
分析:
定义一个USB接口(申明USB接口设备的规范是必须是:可以接入和拔出)
提供2个USB实现类代表鼠标和键盘,让其实现USB接口,并分别定义独有功能
创建电脑对象,创建2个USB实现类对象,分别安装到电脑中并触发功能的执行
//USB接口 == 规范
public interface USB {
//接入、拔出
void connect();
void unconnect();
}
//键盘实现类
public class KeyBoard implements USB {
private String name;
public KeyBoard(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name + "成功连接了电脑~~~");
}
/**
* 独有功能
*/
public void keyDown(){
System.out.println("敲击了:666~~~");
}
@Override
public void unconnect() {
System.out.println(name + "成功从电脑中拔出~~~");
}
}
//键盘实现类
public class Mouse implements USB {
private String name;
public Mouse(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name + "成功连接了电脑~~~");
}
/**
* 独有功能
*/
public void dbClick() {
System.out.println(name + "点击了一键三连!");
}
@Override
public void unconnect() {
System.out.println(name + "成功从电脑中拔出~~~");
}
}
//电脑类
public class Computer {
private String name;
public Computer(String name) {
this.name = name;
}
public Computer() {
}
public void start() {
System.out.println(name + "开机啦~~~");
}
/**
* 提供安装USB设备的接口
*/
public void installUSB(USB usb) {
//多态:usb == 可能是鼠标,也可能是键盘
usb.connect();//接入
//独有功能
if (usb instanceof KeyBoard) {
//键盘
KeyBoard k = (KeyBoard) usb;//强制类型转换
k.keyDown();
} else if (usb instanceof Mouse) {
//鼠标
Mouse m = (Mouse) usb;
m.dbClick();
}
usb.unconnect();//拔出
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 目标:USB设备模拟
* 1、定义USB接口:接入、拔出
* 2、定义2个USB实现类:鼠标、键盘
* 3、创建一个电脑对象,创建USB设备对象,安装启动
*/
public class Test {
public static void main(String[] args) {
//创建电脑对象
Computer c = new Computer("外星人");
c.start();
//创建鼠标、键盘对象
USB usb = new KeyBoard("双飞燕");
c.installUSB(usb);
System.out.println("----------------");
USB usb1 = new Mouse("罗技鼠标");
c.installUSB(usb1);
}
}
内部类
内部类的概述
内部类
内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。
public class People{
//内部类
public classHeart{
}
}
内部类的使用场景、作用
1、当一个事物的内部,还有一个部分需要一个完整的结构进行描述,
而这个内部的完整的结构又只为外部事物提供服务,
那么整个内部的完整结构可以选择使用内部类来设计。
2、内部类通常可以方便访问外部类的成员,包括私有的成员。
3、内部类提供了更好的封装性,内部类本身就可以用private protectecd等修饰,
封装性可以做更多控制。
内部类的分类
静态内部类[了解]
成员内部类(非静态内部类)[了解]
局部内部类[了解]
匿名内部类(重点)
内部类之一:静态内部类[了解]
什么是静态内部类?
有static修饰,属于外部类本身
它的特点和使用与普通类是完全一样的,类有的成分它都有,只是在别人里面而已
public class Outer{
//静态成员内部类
public static class Inner{
}
}
静态内部类创建对象的格式:
格式:外部类.内部类名 对象名 = new 外部类名.内部类构造器;
范例:Outer.Inner in = new Outer.Inner();
静态内部类的访问拓展
1、静态内部类中是否可以直接访问外部类的静态成员?
可以,外部类的静态成员只有一份可以被共享访问。
2、静态内部类中是否可以直接访问外部类的实例成员?
不可以的,外部类的实例成员必须用外部类对象访问。
内部类之二:成员内部类[了解]
什么是成员内部类?
无static修饰,属于外部类的对象
JDK16之前,成员内部类中不能定义静态成员变量,JDK16开始可以定义静态成员变量
public class Inner{
//不用static修饰
public class Inner{
}
}
成员内部类创建对象的格式:
格式:外部类.内部类名 对象名 = new 外部类构造器.new 内部类构造器();
范例:Outer.Inner in = new Outer().new Inner();
成员内部类的访问拓展:
1、成员内部类中是否可以直接访问外部类的静态成员?
可以,外部类的静态成员只有一份可以被共享访问。
2、成员内部类的实例方法中是否可以直接访问外部类的实例成员?
可以的,因为必须先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。
public class Test {
public static void main(String[] args) {
People.Heart heart = new People().new Heart();
heart.show();
}
}
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
}
}
}
内部类之三:局部内部类[了解]
局部内部类(鸡肋语法,了解即可)
局部内部类放在方法、代码块、构造器等执行体中。
局部内部类的类文件名为:外部类$N内部类.class。
内部类之四:匿名内部类概述[重点]
匿名内部类:
本质上是一个没有名字的局部内部类,定义在方法中、代码块中、等。
作用:方便创建子类对象,最终目的为了简化代码编写。
特点总结:
匿名内部类是一个没有名字的内部类
匿名内部类写出来就会产生一个匿名内部类的对象
匿名内部类的对象类型相当于是当前new的那个的类型的子类型
//学习匿名内部类的形式和特点
public class Test {
public static void main(String[] args) {
//Animal a = new Tiger();
//a.run();
//匿名内部类
Animal a1 = new Animal() {
@Override
public void run() {
System.out.println("老虎跑的很快~~~");
}
};
a1.run();
}
}
//老虎子类
//class Tiger extends Animal {
// @Override
// public void run() {
// System.out.println("老虎跑的很快~~~");
// }
//}
//动物抽象类
abstract class Animal {
public abstract void run();
}
匿名内部类常见使用形式
//掌握匿名内部类的使用形式(语法)
public class Test {
public static void main(String[] args) {
Swimming s = new Swimming() {
@Override
public void swim() {
System.out.println("学生在快乐地自由游~~~");
}
};
go(s);
System.out.println("------------------");
Swimming s1 = new Swimming() {
@Override
public void swim() {
System.out.println("老师游的贼快~~~");
}
};
go(s1);
System.out.println("-----------------");
go(new Swimming() {
@Override
public void swim() {
System.out.println("运动员游的贼快啊~~~");
}
});
}
/**
* 老师 学生 远动员一起游泳
*/
public static void go(Swimming s) {
System.out.println("开始游泳~~~");
s.swim();
System.out.println("游泳结束~~~");
}
}
/*学生实现类
class Student implements Swimming{
@Override
public void swim() {
System.out.println("学生在快乐地自由游~~~");
}
}*/
//游泳接口
interface Swimming {
void swim();
}
匿名内部类真实使用场景演示
import javax.swing.*;
import java.awt.event.ActionEvent;
//通过GUI编程 理解匿名内部类的真实使用场景
public class Test {
public static void main(String[] args) {
//1、创建窗口
JFrame win = new JFrame("登录界面");
JPanel panel = new JPanel();//桌布
win.add(panel);//添加桌布
//2、创建一个按钮对象
JButton btn = new JButton("登录");
/*//注意:创建监听对象 匿名内部类的使用
btn.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(win, "点我一下,说明爱我~~");//弹窗
}
});*/
//简化写法
btn.addActionListener(e -> JOptionPane.showMessageDialog(win, "点我一下,说明爱我~~"));
//3、把按钮对象添加到窗口桌布显示
panel.add(btn);
//4、展示窗口
win.setSize(400, 300);//窗口大小
win.setLocationRelativeTo(null);//窗口居中显示
win.setVisible(true);
}
}
常用API
Object
Object类的作用:
一个类要么默认继承了Object类,要么间接继承了Object类,Object类是Java中的祖宗类。
Object类的方法是一切子类都可以直接使用的,所以我们要学习Object类的方法。
Object类的常用方法:
方法名 | 说明 |
public String toString() | 默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址 |
public Boolean equals(Object o) | 默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false |
1. Object的toString方法的作用是什么?
默认是打印当前对象的地址。
让子类重写,以便返回子类对象的内容。
public class Student {
private String name;
private char sex;
private int age;
public Student() {
}
public Student(String name, char sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//方法重构toString()
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex=" + sex +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
Student s = new Student("阿珍", '女', 20);
// String rs = s.toString();
// System.out.println(rs);
//
// System.out.println(s.toString());
System.out.println(s);
}
}
Object的equals方法
存在的意义:父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则
//isNull()方法的使用
import java.util.Objects;
public class Test {
public static void main(String[] args) {
String s1 = new String("Java");
String s2 = null;
System.out.println(Objects.isNull(s1));//false
System.out.println(s1 == null);//false
System.out.println(Objects.isNull(s2));//true
System.out.println(s2 == null);//true
}
}
对象进行内容比较的时候建议使用什么?为什么?
建议使用Objects提供的equals方法
比较结果是一样的,但是更安全
StringBuilder
StringBuilder概述
StringBuilder是一个可变的字符串类,我们可以把它看成是一个对象容器。
作用:提高字符串的操作效率,如拼接、修改等。
StringBuilder 构造器
名称 | 说明 |
public StringBuilder() | 创建一个空白的可变的字符串对象,不包含任何内容 |
public StringBuilder(String str) | 创建一个指定字符串内容的可变字符串对象 |
String Builder常用方法
方法名称 | 说明 |
public StringBuilder append(任意类型) | 添加数据并返回StringBuilder对象本身 |
public StringBuilder reverse() | 将对象的内容反转 |
public int length() | 返回对象内容长度 |
public String toString() | 通过toString()就可以实现把StringBuilder转换为String |
//学会StringBuilder的操作字符
public class Test {
public static void main(String[] args) {
//1、public StringBuilder append(任意类型) 添加数据并返回StringBuilder对象本身
StringBuilder sb1 = new StringBuilder();
sb1.append("a");
sb1.append('b');
sb1.append("ab");
sb1.append(1);
sb1.append(1.5);
sb1.append(false);
System.out.println(sb1);//abab11.5false
StringBuilder sb2 = new StringBuilder();
//支持链式编程
sb2.append("abc").append(3).append(5.2).append("Java");
System.out.println(sb2);//abc35.2Java
//2、public StringBuilder reverse() 将对象的内容反转
sb1.reverse().append("我爱你~");
System.out.println(sb1);//eslaf5.11baba我爱你~
//3、public int length() 返回对象内容长度
System.out.println(sb2.length());//11
//注意:StringBuilder只是拼接字符串的手段:效率高
//最终的结果还是要恢复成String类型
StringBuilder sb = new StringBuilder();
sb.append("123").append("456");
//check(sb);//sb是StringBuiler类型不是String类型,不能进行传递
//恢复成String
String rs = sb.toString();
check(rs);
}
public static void check(String data) {
}
}
1、为什么拼接、反转字符串建议使用StringBuilder?
String:内容是不可变的、拼接字符串性能差。
String Builder:内容是可变的、拼接字符串性能好、代码优雅。
定义字符串使用String
拼接、修改等操作字符串使用StringBuilder
案例:打印整型数组内容
需求:
设计一个方法用于输出任意整型数组的内容,要求输出以下格式:
"[11, 22, 12]"
分析:
定义一个方法,要求该方法能够接收数组,并输出数组内容(需要参数和返回值类型)
//学会StringBuilder的操作字符
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
int[] arr = null;
int[] arr1 = {};
int[] arr2 = {11, 22, 12};
System.out.println(toString(arr));//null
System.out.println(toString(arr1));//[]
System.out.println(toString(arr2));//[11, 22, 12]
}
public static String toString(int[] arr) {
if (arr != null) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]).append(i == arr.length - 1 ? "" : ", ");
}
sb.append("]");
return sb.toString();
} else {
return null;
}
}
}
Math
Math类
包含执行基本数字运算的方法,Math类没有提供公开的构造器。
如何使用类中的成员呢?看类的成员是否都是静态的,如果是,通过类名就可以直接调用
Math类的常用方法
方法名 | 说明 |
public static int abs(int a) | 获取参数绝对值 |
public static double ceil (double a) | 向上取整 |
public static double floor(double a) | 向下取整 |
public static int round (float a) | 四舍五入 |
public static int max(int a,int b) | 获取两个int值中的较大值 |
public static double pow(double a,double b) | 返回a的b次幂的值 |
public static double random() | 返回值为double的随机值,范围[0.0,1.0) |
public class Test {
public static void main(String[] args) {
//1、public static int abs(int a) 获取参数绝对值
System.out.println(Math.abs(10));//10
System.out.println(Math.abs(-10.5));//10.5
//2、public static double ceil (double a) 向上取整
System.out.println(Math.ceil(4.00000001));//5
System.out.println(Math.ceil(4.99999999));//5
//3、public static double floor(double a) 向下取整
System.out.println(Math.floor(6.9999999999));//6
System.out.println(Math.floor(6.00000001));//6
//4、求指数次方
System.out.println(Math.pow(2, 3));//2^3=8.0
//5、四舍五入
System.out.println(Math.round(4.46));//4
System.out.println(Math.round(4.54));//5
//6、public static double random() 返回值为double的随机值,范围[0.0,1.0)
System.out.println(Math.random());
}
}
System
System 类概述
System的功能是通用的,都是直接用类名调用即可,所以System不能被实例化。
System 类的常用方法
方法 | 说明 |
public static void exit(int status) | 终止当前运行的Java虚拟机,非零表示异常终止 |
public static long currentTimeMillis() | 返回当前系统的时间毫秒值形式 |
public static void arraycopy(数据源数组,起始索引,目的地数组,起始索引,拷贝个数) | 数组拷贝 |
BigDecimal
BigDecimal作用
用于解决浮点型运算精度失真的问题
使用步骤
创建对象BigDecimal封装浮点型数据(最好的方式是调用方法)
public static BigDecimal valueOf(double val): 包装浮点数成为BigDecimal对象。
BigDecima常用API
方法名 | 说明 |
public BigDecimal add(BigDecimal b) | 加法 |
public BigDecimal subtract(BigDecimal b) | 减法 |
public BigDecimal multiply(BigDecimal b) | 乘法 |
public BigDecimal divide(BigDecimal b) | 除法 |
public Big Decimal divide(另一个BigDecimal对象,精确几位,舍入模式) | 除法 |
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Test {
public static void main(String[] args) {
System.out.println(0.3+0.4);//0.7
System.out.println(0.3*0.2);//0.06
System.out.println("-------------------");
double a = 0.1;
double b = 0.2;
double c = a + b;
System.out.println(c);//0.30000000000000004
System.out.println("-------------------");
//包装浮点数据成为大数据对象BigDecimal
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = BigDecimal.valueOf(b);
BigDecimal c1 = a1.add(b1);
System.out.println(c1);//0.3
//目的:double
double rs = c1.doubleValue();
System.out.println(rs);//0.3
System.out.println("-------------------");
/*BigDecimal a11 = BigDecimal.valueOf(10.0);
BigDecimal b11 = BigDecimal.valueOf(3.0);
BigDecimal c11 = a11.divide(b11);//报错,3.333333
System.out.println(c11);*/
//注意事项:BigDecimal是一定要精度运算的
BigDecimal a11 = BigDecimal.valueOf(10.0);
BigDecimal b11 = BigDecimal.valueOf(3.0);
//参数一:除数 参数二:保留小数位数 参数三:舍入模式
BigDecimal c11 = a11.divide(b11,2, RoundingMode.HALF_UP);
System.out.println(c11);//3.33
}
}