文章目录
1. 包
1.1 什么是包?
-
包是用来分门别类的管理各种不同类(java文件)的,类似于文件夹、建包利于程序的管理和维护
-
建包的语法格式:package 公司域名倒写.技术名称。包名建议全部英文小写,且具备意义
-
package com.itheima.domain; // 存储Javabean 起名字叫domain public class Student{}
-
建包语句必须在第一行, 一般IDEA工具会帮助创建
1.2 导包
- 相同包下的类可以直接访问, 不同包下的类必须导包,才可以使用!(java.lang包的除外)
- 格式: import 包名.类名;
- 假如一个类中需要用到不同的类,而这两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问
2. 多态
2.1 多态的概述
- 什么是多态
- 在继承/实现关系中, 不同子类的对象会展现各自的行为.这种现象叫多态
- 前提
- 有继承/实现关系
- 有方法的重写
- 有父类的引用指向子类的对象: (父类的类型可以记录任意子类的对象)
- 格式: 父类名 变量名 = new 子类名();
- 示例: Fu f = new Zi();
- 注意: 成员变量没有多态
- 代码演示
- 父类: 人(Person)的类
- 成员变量: 年龄(age)
- 方法: 跑(run)
- 子类: 老师类(Teacher)
- 成员变量: 年龄(age)
- 方法:
- 跑(run): 老师跑的慢
- 教学(teach): 老师在上课
- 子类: 学生类(Student)
- 成员变量: 年龄(age)
- 方法:
- 跑(run): 学生跑的快
- 学习(study): 学生在学习
- 父类: 人(Person)的类
public class Person {
public int age = 10;
public void run(){
System.out.println("跑");
}
}
public class Student extends Person{
public int age = 23;
@Override
public void run() {
System.out.println("学生跑的快~~~");
}
public void study() {
System.out.println("好好学习~~~");
}
}
public class Teacher extends Person{
public int age = 40;
@Override
public void run() {
System.out.println("老师跑的慢~~~~~");
}
public void teach() {
System.out.println("好好上课~~~");
}
}
public class Demo1 {
/*
在继承/实现关系中 不同子类的对象会展现各自的行为.这种现象就是多态
前提:
1.要有继承/实现关系
2.要有方法的重写
3.父类的引用指向子类的对象(父类类型的变量可以记录任意子类的对象)
父类类型 变量名 = 子类的对象
Fu f = new Zi();
不同子类会展现各自的行为
Person p = new Teacher(); p.run() 执行Teacher中的run方法
Person p = new Student(); p.run() 执行Student中的run方法
注意: 成员变量没有多态现象
*/
public static void main(String[] args) {
Person p = new Student();
// Person p = new Teacher();
p.run();
System.out.println(p.age); // 成员变量没有多态现象
}
}
1.2 多态的好处和弊端
好处:多态形势下,右边的对象是解耦合的,便于扩展和维护
People p = new Teacher()/new Student() ....;
p.run();
常见的使用场景: 定义方法时,使用父类类型的参数,可以接收一切子类的对象,提高扩展性
public class Demo2 {
public static void main(String[] args) {
Student s = new Student();
show(s);
Teacher t = new Teacher();
show(t);
}
// 多态的好处: 定义方法时,使用父类类型的参数,可以接受一切子类的对象,提高扩展性
public static void show(Person p){ // Person p = ?
p.run();
}
}
弊端:不能使用子类特有的方法
public class Demo2 {
public static void main(String[] args) {
Student s = new Student();
show(s);
Teacher t = new Teacher();
show(t);
}
public static void show(Person p){ // Person p = ?
p.run();
// 多态的弊端: 不能使用子类特有的方法
// p.study(); // 编译错误
// p.teach(); // 编译错误
}
}
1.3 类型转换
- 多态的弊端: 无法使用子类特有的方法
- 如何解决? 强制类型转换
1.4 类型转换语法
- 自动类型转换
- 父类名 变量名 = new 子类();
- Person p = new Teacher();
- 强制类型转换
- 子类 变量名 = (子类) 父类变量
- Teacher t = (Teacher)p;
Student s = (Student) p;
s.study();
ClassCastException
- 强制类型转换,有可能出现类型转换异常ClassCastException
- 原因: 运行时,变量记录的真实类型与强转后的类型不同
Person p = new Teacher(); // p变量记录的真实类型为Teacher
Student s = (Student)p; // 与强转后Student的类型不一致,ClassCastException
instanceof
- 作用: 判断真实类型,避免类型转换异常
- p instanceof Student
- 涵义: 判断p变量记录的真实类型 是否为 Student
- 如果是: 结果为true
- 如果不是: 结果为false
- 涵义: 判断p变量记录的真实类型 是否为 Student
if(p instanceof Student){
// 如果p记录的真实类型是Student,再强转
Student s = (Student) p;
s.study();
}
if(p instanceof Teacher){
// 如果p记录的真实类型是Teacher,再强转
Teacher t = (Teacher) p;
t.teach();
}
1.5 练习
- 定义动物类(Animal)
- 成员变量:昵称(nickname),年龄(age)
- 构造方法:无参,带参
- 成员方法:
- get/set
- 吃东西(eat)
- 打印=> 动物吃东西
- 定义猫类(Cat),继承Animal
- 构造方法:无参,带参
- 成员方法:
- 重写吃(eat) , 打印 => 猫吃鱼
- 捉老鼠(catchMouse), 打印=> 猫会捉老鼠
- 定义狗类(Dog),继承Animal
- 构造方法:无参,带参
- 成员方法:
- 重写吃(eat) , 打印 => 狗吃肉
- 看门(watch), 打印=> 狗会看门
- 定义测试类(Demo),写代码测试
- 在main方法平级的位置 定义 静态方法show
- 参数: (Animal a)
- 代码
- 调用a.eat();
- 使用instanceof关键字判断 a 记录的真实类型
- 如果a 记录的是Dog, 调用看门(watch)方法
- 如果a 记录的是Cat,调用捉老鼠(catchMouse)方法
- 在main方法中调用show方法,传递不同的对象,观察程序运行结果
public class Animal {
private String nickname;
private int age;
public Animal(String nickname, int age) {
this.nickname = nickname;
this.age = age;
}
public Animal() {
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(){
System.out.println("动物吃东西");
}
}
public class Dog extends Animal{
public Dog(String nickname, int age) {
super(nickname, age);
}
public Dog() {
}
@Override
public void eat() {
System.out.println(getNickname() + "吃肉");
}
public void watch(){
System.out.println(getNickname() + "看门");
}
}
public class Cat extends Animal{
public Cat(String nickname, int age) {
super(nickname, age);
}
public Cat() {
}
@Override
public void eat() {
System.out.println(getNickname() + "吃鱼");
}
public void catchMouse(){
System.out.println(getNickname() +"捉老鼠");
}
}
public class Demo {
public static void main(String[] args) {
// show(new Cat("加菲猫",3));
show(new Dog("哈士奇",4));
}
public static void show(Animal a){
a.eat();
if (a instanceof Dog){
Dog d = (Dog) a;
d.watch();
} else if (a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzVoBw2K-1681896245356)(E:\itcast\images\summarize\Image1.png)]
3.抽象类
3.1 认识抽象方法和抽象类
- 多态中案例中子父类存在的问题?
- 图示
- abstract关键字
- 修饰方法: 抽象方法,不写方法体
- 修饰类: 抽象类, 有抽象方法的类,必须是抽象类
public abstract class Person {
public abstract void run();
}
3.2 抽象类的特点
- 抽象类可以没有抽象方法,有抽象方法的类必须是抽象类
- 抽象类中的成员(成员变量,构造方法,成员方法)都可以有
- 和普通的类最主要的区别: 不能创建对象,作为一种特殊的父类,让子类继承并实现
- 抽象类的子类
- 要么重写所有的抽象方法
- 要么子类也定义为抽象类
public abstract class A {
private String name;
public abstract void test();
public A(){
}
public void show() {
System.out.println("show方法执行了 ....");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class B extends A{
@Override
public void test(){
System.out.println("重写的test方法执行了")
}
}
public class Demo {
public static void main(String[] args) {
// A a = new A(); // 编译错误 抽象类不能创建对象
B b = new B();
b.show();
System.out.println(b.getName());
b.test();
}
}
3.3 抽象方法关键字使用冲突
- private: private 修饰的方法,子类不能重写 - abstract 要求子类必须重写
- final: final 修饰的方法,子类不能重写 - abstract 要求子类必须重写
- static: static 修饰的方法,子类不能重写 - abstract 要求子类必须重写
3.4 模板方法设计模式
- 解决什么问题: 子类中重复的代码
public class A{
public void sing(){
System.out.println("下面给大家带来一首攒劲的歌曲: ");
System.out.println("我是一只小小鸟,想要飞却怎么样也飞不高~~~~~~~~");
System.out.println("....小小鸟歌词....");
System.out.println("....小小鸟歌词....");
System.out.println("....小小鸟歌词....");
System.out.println("谢谢!");
}
}
public class B{
public void sing(){
System.out.println("下面给大家带来一首攒劲的歌曲: ");
System.out.println("我们一起学猫叫,一起喵喵喵喵喵~~~~~~~~~~");
System.out.println("....学猫叫歌词....");
System.out.println("....学猫叫歌词....");
System.out.println("....学猫叫歌词....");
System.out.println("谢谢!");
}
}
- 思路
- 问题: 所有的功能都挤在同一个方法中,过于臃肿.
- 封装的思想,将代码抽取到方法中,按照功能进行分类管理
- 将歌词的部分抽取成方法song(歌曲)
- 两个类中共性的内容(sing和song)抽取到父类(Fu)中
- sing方法中的内容子类中都是相同的,所以子类不需要重写
- song方法中的歌词子类中是不同的,子类需要重写,父类中的song方法可以定义成抽象方法
- 编写测试类
- 在main方法平级的位置定义show方法,参数为父类类型(Fu f)
- 在show方法中调用模板方法f.sing
- 在main方法中调用show方法 传递不同的子类对象观察效果
- 问题: 所有的功能都挤在同一个方法中,过于臃肿.
public abstract class Fu {
public void sing(){
System.out.println("下面给大家带来一首攒劲的歌曲: ");
song();
System.out.println("谢谢!");
}
public abstract void song() ;
}
public class A extends Fu{
@Override
public void song() {
System.out.println("我是一只小小鸟,想要飞却怎么样也飞不高~~~~~~~~");
System.out.println("....小小鸟歌词....");
System.out.println("....小小鸟歌词....");
System.out.println("....小小鸟歌词....");
}
}
public class B extends Fu{
@Override
public void song() {
System.out.println("我们一起学猫叫,一起喵喵喵喵喵~~~~~~~~~~");
System.out.println("....学猫叫歌词....");
System.out.println("....学猫叫歌词....");
System.out.println("....学猫叫歌词....");
}
}
public class Demo {
public static void main(String[] args) {
show(new B());
}
public static void show(Fu f){
f.sing();
}
}
4. 接口
4.1 概述
- 现实生活中的规则
- 笔记本电脑规定USB插口形状
- 其他设备必须按照这个形状做USB插头
- 代码层面
- 如果抽象类中只有抽象方法,那么我们就可以将这个抽象类改写为接口
- 接口主要是对行为制定规则
public abstract class A{
public abstract void method1();
public abstract void method2();
}
public interface A{
public abstract void method1();
public abstract void method2();
}
- 接口的特点
- 接口使用关键字interface来定义:
- public interface 接口名{}
- 接口不能实例化
- 接口和类之间是实现关系,通过implements关键字表示
- public class 类名 implements 接口名{}
- 接口的实现类
- 要么重写所有的抽象方法
- 要么是抽象类
- 接口使用关键字interface来定义:
public interface A {
public abstract void method1();
public abstract void method2();
}
/*
B类称为实现类
实现类:
要么重写所有的抽象方法
要么将实现类变成抽象类
*/
public class B implements A{ // 实现关系,B类实现了A接口
@Override
public void method1() {
}
@Override
public void method2() {
}
}
public class Demo {
public static void main(String[] args) {
// A a = new A(); // 接口不能实例化
}
}
4.2 接口中成员的特点
- 构造方法: 没有
- 成员变量: 只能是常量(final)
- 默认修饰符: public final static
- 成员方法: 只能是抽象方法
- 默认修饰符: public abstract
- 接口中的方法JDK8和JDK9有一些新特性
/*
接口中成员的特点
构造方法: 没有
成员变量: 都是常量
默认修饰符 public final static
成员方法: 只能是抽象方法
默认修饰符 public abstract
接口中的方法,在JDK8和JDK9增加了新的方法
*/
public interface A {
public static final int a = 10;
public abstract void show();
}
public class Demo {
public static void main(String[] args) {
System.out.println(A.a);
}
}
4.3 类和接口的各种关系
- 类和类的关系
- 继承关系,只能是单继承,支持多层继承
- 类和接口的关系
- 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口和接口的关系
- 继承关系,可以单继承,也可以多继承
public interface Inter1 {}
public interface Inter2 {}
public class Fu{}
public class InterImpl extends Fu implements Inter1,Inter2 {}
public interface C {
}
public interface D {
}
public interface E extends D,C {
}
4.4 案例
- 木门和电动报警门
- ①木门
- 成员变量:宽,高,品牌
- 成员方法:开门,关门
- ②电动报警门:
- 成员变量:宽,高,品牌
- 成员方法:开门,关门,报警
分析
实现
- ①定义报警 (Alarm)接口
- 成员方法:报警
- ②定义门 (Door)抽象类
- 成员变量:宽,高,品牌
- 构造方法:无参,带参
- 成员方法:get/set方法,开门,关门
- ③定义木门类(WoodDoor),继承门类
- 构造方法:无参,带参
- 成员方法:开门,关门
- ④定义电动报警门类(ElectricAlarmDoor),继承门类,实现报警接口
- 构造方法:无参,带参
- 成员方法:开门,关门,报警
- ⑤测试类中创建对象进行测试
public interface Alarm {
void alarm();
}
public abstract class Door {
private double weight;
private double height;
private String brand;
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Door(double weight, double height, String brand) {
this.weight = weight;
this.height = height;
this.brand = brand;
}
public Door() {
}
public abstract void open();
public abstract void close();
}
public class WoodDoor extends Door{
public WoodDoor(double weight, double height, String brand) {
super(weight, height, brand);
}
public WoodDoor() {
}
@Override
public void open() {
System.out.println("手动开门");
}
@Override
public void close() {
System.out.println("手动关门");
}
}
public class ElectricAlarmDoor extends Door implements Alarm{
public ElectricAlarmDoor(double weight, double height, String brand) {
super(weight, height, brand);
}
public ElectricAlarmDoor() {
}
@Override
public void alarm() {
System.out.println("滴滴滴.....");
}
@Override
public void open() {
System.out.println("自动开门");
}
@Override
public void close() {
System.out.println("自动关门");
}
}
public class Demo {
public static void main(String[] args) {
WoodDoor w = new WoodDoor(1.2,2.2,"TATA");
showDoor(w);
System.out.println("==================");
ElectricAlarmDoor e = new ElectricAlarmDoor(6.0,3.0,"盼盼");
showDoor(e);
showAlarm(e);
}
// 定义方法 参数类型为门,接收一切子类对象
public static void showDoor(Door d){
System.out.println("宽: " + d.getWeight() + ",高: " + d.getHeight() + ", 品牌: " + d.getBrand());
d.open();
d.close();
}
// 定义方法 参数为警报器,接收一切实现类对象
public static void showAlarm(Alarm a){ // 接收一切实现类对象 接口名 变量名 = new 实现类名()
a.alarm();
}
}
目标
- 理解接口制定了行为的规则: 实现Alarm接口,就必须具备alarm功能
- 理解继承一个类的同时,继续实现接口,可以实现功能的扩展
4.5 接口中新增的方法[了解]
- 默认方法: JDK8
- 使用default关键字修饰,默认public修饰
- 注意: 只能由接口的实现类对象调用
- 实现类可以直接使用,也可以重写
- 静态方法: JDK8
- 使用static修饰,默认public修饰
- 注意: 只能用接口名调用
- 私有方法: JDK9
- 只能用private修饰
- 作用: 增强了接口的能力,便于项目维护和扩展
public interface A {
public default void show1(){
System.out.println("我是show1方法");
show3();
}
public static void show2(){
System.out.println("我是show2方法");
show3();
}
private static void show3(){
System.out.println("我是show3方法");
}
}
public class B implements A{
@Override
public void show1() {
System.out.println("我是实现类中的show1方法");
}
}
public class Demo {
public static void main(String[] args) {
B b = new B();
// b.show2();
b.show1();
A.show2(); // 使用方便
}
}