目录
static关键字
1.介绍
修饰符,是静态的意思,可以修饰成员变量,也可以修饰成员方法
2.特点
-
被其修饰的成员,被该类的所有对象所共享
-
多了一种调用方式,可以通过类名调用(推荐)
-
随着类的加载而加载,优先于对象存在
package com.itheima.mstatic; public class Student { String name; int age; //"传智专修学院"被所有的对象共享 static String school; }
package com.itheima.mstatic; public class StaticDemo1 { public static void main(String[] args) { //可以通过类名调用(推荐使用) Student.school = "传智专修学院"; Student stu1 = new Student(); stu1.name = "张三"; stu1.age = 23; System.out.println(stu1.name + "---" + stu1.age + "---" + Student.school); System.out.println("---------------------"); Student stu2 = new Student(); stu2.name = "李四"; stu2.age = 24; System.out.println(stu2.name + "---" + stu2.age + "---" + Student.school); } }
运行结果
3.static修饰成员变量内存图解
4.使用场景
-
static成员变量:共享数据
-
static成员方法:常用于工具类
5.static修饰成员方法——工具类——注意事项
-
成员方法什么时候加入 static --> 常用于制作工具类
-
工具类: 不是描述事物的, 而是帮我们完成一些事情 (打工)
-
如果发现一个类中, 所有的方法, 全都是 static 所修饰——私有该类的构造方法 (目的: 为了不让其他类, 再创建对象)
package com.itheima.tools; //工具类,为调用者提供服务 public class ArrayTools { //私有这个工具类的构造方法(不让别人再去创建对象:static是静态的,用类名可以直接调用) private ArrayTools(){} public static int getMax(int[] arr) { int max = arr[0]; for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } return max; } public static int getMin(int[] arr) { int min = arr[0]; for (int i = 1; i < arr.length; i++) { if (arr[i] < min) { min = arr[i]; } } return min; } public static void printArray(int[] arr) { System.out.print("["); for (int i = 0; i < arr.length - 1; i++) { System.out.print(arr[i] + ", "); } System.out.println(arr[arr.length - 1] + "]"); } }
package com.itheima.tools; public class Test { public static void main(String[] args) { int[] arr = {11,22,33}; //**类名.方法名(因为getMax和getMin有静态变量static修饰) //不需要创建对象:ArrayTools tools = new ArrayTools(); System.out.println(ArrayTools.getMax(arr)); System.out.println(ArrayTools.getMin(arr)); ArrayTools.printArray(arr); //终止当前Java虚拟机正在运行的任务 System.exit(0); } }
-
static方法中,只能访问静态成员(直接访问)(非静态需要把对象new出来,要不然在内存中还没有)
-
static中不允许使用this关键字
package com.itheima.mstatic; public class StaticDemo2 { static int num1 = 10; int num2 = 20; public static void method(){ System.out.println("static...method"); } public void print(){ System.out.println("print..."); } public static void main(String[] args) { // 在静态方法中, 只能访问静态成员(直接访问) System.out.println(num1); method(); //解决办法:创建对象后能够使用 StaticDemo2 sd = new StaticDemo2(); System.out.println(sd.num2); sd.print(); } }
ps:重新认识main方法
继承
1.介绍
让类与类之间产生关系(子父类关系),子类可以直接使用父类中非私有的成员
2.格式
public class 子类名 extends 父类名 { }
范例:public class Zi extends Fu { }
Fu:是父类,也被称为基类、超类
Zi:是子类,也被称为派生类
创建类的细节:一个java文件中可以编写多个class(不建议使用)
保证类与类之间是平级关系
只有一个能被public修饰
package com.itheima.mextends; public class ExtendsDemo1 { /* 继承: 类与类之间产生关系(子父类关系), 子类就可以直接使用父类中的非私有的成员 */ public static void main(String[] args) { Coder c = new Coder(); c.setName("张三"); c.setAge(23); c.setSalary(12000); System.out.println(c.getName() + "---" + c.getAge() + "---" + c.getSalary()); } } class Employee { private String name; private int age; private double salary; //虽然父类中的私有了,但set和get方法中是公有的,子类可以继承使用 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 double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } } class Coder extends Employee { } class Manager extends Employee { }
3.使用场景
好处:提高代码的复用性
使用场景:当类与类之间,存在相同(共性)的内容,并且产生了谁is谁的a一种的关系,就可以考虑使用继承,来优化代码。
4.继承中成员变量和成员方法的访问特点
①成员变量
子父类中,如果出现了重名的成员变量,根据就近原则,优先使用子类的
使用super关键字来进行解决
this:调用本类成员
super:调用父类成员
public class Fu { int num = 10; }
class Zi extends Fu { int num = 20; public void method(){ int num = 30; System.out.println(num);// 30 System.out.println(this.num);// 20 System.out.println(super.num);// 10 } }
②成员方法
在继承体系中,子类可以继承到父类的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写,方法重写又称方法覆盖
方法重载(Overload) : 在同一个类中, 方法名相同, 参数不同, 与返回值无关 ---> 参数不同: 类型不同, 个数不同, 顺序不同
方法重写(Override) : 在子父类当中, 出现了方法声明一模一样的方法 (方法名, 参数, 返回值)
-
目标1: 能够独立识别出, 方法是不是重写的方法 (在子类的方法上面加注解:@Override,如果不报错则为方法重写)
-
目标2: 方法重写的使用场景 ---> 当子类需要父类的方法, 但是觉得父类的方法逻辑不好 (修改 | 增强) 就可以对父类的方法进行重写
package com.itheima.mextends; public class ExtendsDemo3 { public static void main(String[] args) { Son s = new Son(); s.love(); } } class Father { public void love() { System.out.println("送花"); System.out.println("送肉"); System.out.println("唱歌"); } } class Son extends Father { @Override public void love() { System.out.println("送酱肉包"); System.out.println("送口红"); System.out.println("我喜欢你, 一起去爬山吗~"); } }
运行结果
送酱肉包
送口红
我喜欢你, 一起去爬山吗~
注意事项:
-
父类中私有方法不能被重写
-
子类重写父类方法时,访问权限必须大于等于父类
修饰符 | 类本身 | 同一个包的类 | 不同包的子类 | 任何类 |
---|---|---|---|---|
private | √ | |||
default(默认) | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
5.Java中继承的特点
-
Java只支持单继承,不支持多继承,但支持多层继承
package com.itheima.mextends; public class ExtendsDemo4 { public static void main(String[] args) { C c = new C(); c.methodC(); c.methodB(); c.methodA(); } } class A { public void methodA() { System.out.println("A..."); } } class B extends A { public void methodB() { System.out.println("B..."); } } class C extends B { public void methodC() { System.out.println("C..."); } }
6.继承中构造方法访问特点
-
父类中的构造方法能否被继承?
不能,子类需要自己手动编写构造方法
-
子类在初始化之前,是否需要先完成父类的初始化?
需要,因为子类可能会调用到父类的数据
-
如何完成父类的初始化?
构造方法用于对象初始化,子类只要有办法调用到父类的构造方法即可
-
如何调用父类的构造方法?
除了Object类,在所有的构造方法的第一行代码中,都默认隐藏了一句话super();通过这句代码,来访问父类的空参构造方法,从而完成父类的初始化
细节: Java当中所有的类, 都直接或者间接的继承到了 Object 类(父类也默认继承了Object类)
package com.itheima.mextends.constructor; public class Test { public static void main(String[] args) { Zi z1 = new Zi(); Zi z2 = new Zi(10); } } class Demo {} class Fu { public Fu() { //父类也默认继承了Object类 super(); System.out.println("Fu类的空参构造方法"); } public Fu(int num) { System.out.println("Fu类的带参构造方法..."); } } class Zi extends Fu { public Zi() { super(); System.out.println("Zi类的空参构造方法"); } public Zi(int num) { super(); System.out.println("Zi类的带参构造方法..."); } }
运行结果
Fu类的空参构造方法
Zi类的空参构造方法
Fu类的空参构造方法
Zi类的带参构造方法...
7.继承的案例
需求: 人类: (Person) 成员变量: 姓名, 年龄
老师类: 成员变量: 姓名, 年龄 成员方法: teach
姓名为张三, 年龄为30岁的老师正在讲课
学生类: 成员变量: 姓名, 年龄, 成绩 成员方法: study
姓名为李四, 年龄为20岁, 成绩为100分的学生, 正在学习
父类:Person
package com.itheima.test; public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } }
子类:Teacher、Student
package com.itheima.test; public class Teacher extends Person{ public Teacher() { } public Teacher(String name, int age) { super(name, age); } public void teach() { System.out.println("姓名为" + super.getName() +",年龄为" + super.getAge() + "的老师正在讲课"); } }
package com.itheima.test; public class Student extends Person{ private double score; public Student() { } public Student(String name, int age, double score) { super(name, age); //父类没有,需要自己初始化 this.score = score; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } public void study() { //成绩没有必要调用方法,score成员变量虽然被私有化,但它的使用环境就在本类当中 System.out.println("姓名为" + super.getName() + ",年龄为" + super.getAge() + "岁,成绩为" + score + "分的学生,正在学习"); } }
测试类
package com.itheima.test; public class Test1 { public static void main(String[] args) { Teacher t = new Teacher("张三", 30); t.teach(); Student stu = new Student("李四", 20, 100); stu.study(); } }
运行结果
姓名为张三,年龄为30的老师正在讲课 姓名为李四,年龄为24岁,成绩为100.0分的学生,正在学习
8.继承中构造方法执行流程的内存图解
注意:父类中私有的变量子类可以继承,但由于权限的不足,无法直接使用
9.综合案例
父类 --> Employee
package com.itheima.test; public class Employee { private String name; private int age; private double salary; public Employee() { } public Employee(String name, int age, double salary) { this.name = name; this.age = age; this.salary = salary; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } /** * 获取 * @return salary */ public double getSalary() { return salary; } /** * 设置 * @param salary */ public void setSalary(double salary) { this.salary = salary; } public void work() { System.out.println("员工工作..."); } }
子类 --> Coder、Manager
package com.itheima.test; public class Coder extends Employee{ public Coder() { } public Coder(String name, int age, double salary) { super(name, age, salary); } //重写父类方法 @Override public void work() { //子类没有get...的方法,所以super.可以省略 System.out.println("姓名为" + super.getName() + ",年龄为" + super.getAge() + ",工资为" + super.getSalary() + "的程序员正在编写代码"); } }
package com.itheima.test; public class Manager extends Employee{ private double bonus; public Manager() { } public Manager(String name, int age, double salary, double bonus) { super(name, age, salary); //父类没有的自己初始化 this.bonus = bonus; } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } @Override public void work() { //子类没有get...的方法,所以super.可以省略 System.out.println("姓名为" + super.getName() + ",年龄为" + super.getAge() + ",工资为" + super.getSalary() + ",奖金为" + bonus + "的项目经理正在分配任务"); } }
测试类
package com.itheima.test; public class Test2 { public static void main(String[] args) { Coder c = new Coder("张三",23,15000); c.work(); Manager m = new Manager("李四",24,18000,5000); m.work(); } }
10.this和super
this:代表本类对象的引用
super:代表父类存储空间的标识
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.本类成员变量; | this.本类成员方法(); | this(); this(…);本类构造方法 |
super | super.父类成员变量; | super.父类成员方法(); | super(); super(…);父类构造方法 |
被调用的变量和方法,如果在子类中不存在,那么"super."可以直接省略
注意:this()和super()都在争夺构造方法第一行的位置,所以二者不能共存
开闭原则:对功能扩展做开放,对修改代码做关闭(尽量不要修改已经编写好的代码,如果有新功能,重新编写)
final关键字
1.介绍
是最终的意思,可以修饰(方法,类,变量)
2.特点
-
修饰方法:表明该方法是最终方法,不能被重写
-
修饰类:表明该类是最终类,不能被继承
-
修饰变量:表明该变量是常量,不能再次被赋值
基本数据类型:数据值不可改变
引用数据类型:地址值不可改变,内容可以改变
3.final修饰成员变量的注意事项
-
final修饰成员变量时,不允许修饰默认值
-
final修饰成员变量的初始化时机:
①在定义的时候直接赋值
class Student{ final int num = 10; }
②在构造方法中完成赋值(不能在普通成员方法中赋值 public void study(){num = 2;})
class Student{ final int num = 10; public Student() { num = 20; } }
4.final修饰变量的命名规范
如果变量名是一个单词,所有字母大写 max -->MAX
如果变量名是多个单词,所有字母大写,中间使用下划线(_)分割 maxValue --> MAX_VALUE
面向对象高级
一、包
1.介绍
包本质来说就是文件夹,用来管理类文件的
-
建包的语法格式:package公司域名倒写.技术名称。包名建议全部英文小写,且具备意义
package com.itheima.domain; public class Student { }
-
建包语句必须在第一行,一般IDEA工具会帮助创建
2.导包
-
相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!
-
导包格式:import包名.类名;
-
假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。--> 使用全类名创建对象:包名 + 类名
a包
package com.itheima.a; public class Student { public void eat() { System.out.println("学生吃饭"); } }
b包
package com.itheima.b; public class Student { public void sleep() { System.out.println("学生睡觉"); } }
测试类
package com.itheima.c; import com.itheima.a.Student; public class Test { public static void main(String[] args) { Student stu1 = new Student(); stu1.eat(); //创建b包下的Student对象时报错:com.itheima.b.Student,系统不会自动导入b包代码 //使用全类名创建对象:包名 + 类名 com.itheima.b.Student stu2 = new com.itheima.b.Student(); stu2.sleep(); } }
二、抽象类
1.介绍
抽象类是一种特殊的父类,内部可以编写抽象方法
当我们将共性的方法, 抽取到父类之后, 发现这个方法的实现逻辑在父类中无法给出具体明确(描述不清了),而且这个方法, 还是子类必须要有的方法, 就可以设计为抽象方法.
package com.itheima.mabstract; public class AbstractTest1 { public static void main(String[] args) { } } /** * 父类描述不清子类必须要有的东西 class Animal { public void eat() { System.out.println("????"); } } */ //抽象方法要放到抽象类中,因此class前也要加 abstract abstract class Animal { public abstract void eat(); } //抽象类的子类必须要重写方法 class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } } class Dog extends Animal { @Override public void eat() { System.out.println("狗吃肉"); } }
2.格式
①抽象方法的定义格式
public abstract 返回值类型 方法名(参数列表);
②抽象类的定义格式
public abstract class 类名 { }
3.注意事项
-
抽象类不能实例化(如果抽象类允许创建对象,就可以调用内部没有方法体的抽象方法了)
-
抽象类存在构造方法(存在意义:交给子类,通过super进行访问)
-
抽象类中可以存在普通方法(存在意义:可以让子类继承到继续使用)
-
抽象类的子类:要么重写抽象类中的所有抽象方法,要么变成抽象类
4.abstract关键字的冲突
-
final:被abstract修饰的方法,强制要求子类重写,被final修饰的方法子类不能重写
-
private:被abstract修饰的方法,强制要求子类重写,被private修饰的方法子类不能重写
-
static:被static修饰的方法可以类名调用,类名调用抽象方法没有意义
三、接口
1.介绍
接口体现的思想是对规则的声明,Java中的接口更多体现的是对行为的抽象(抽象方法)。
思路:如果发现一个类,所有的组成都是抽象方法,没有成员变量,没有普通方法,这种类,我们通常会设计为java中的接口(因为这个类唯一存在的价值就是声明规则了)
2.定义格式
public interface 接口名{ }
3.注意事项
①接口不允许实例化(new对象):Inter i = new Inter();
②接口和类之间是实现关系,通过implements关键字完成
-
格式:class 类名 implements 接口名 { }
③实现类(接口的子类)
-
重写所有的抽象方法
-
将实现类变成抽象类(abstract)
package com.itheima.minterface; import javax.swing.*; public class InterfaceTest1 { public static void main(String[] args) { //接口不允许实例化(new对象):Inter i = new Inter(); /** * 接口和类之间是实现关系,通过implements关键字完成 * 格式:class 类名 implements 接口名 { } */ //创建实现类对象 InterImpl ii = new InterImpl(); ii.method(); ii.show(); } } //接口是声明规则的,里面只能编写抽象方法 interface Inter { public abstract void show(); public abstract void method(); } //作为实现类(接口的子类),要重写接口里的抽象方法 class InterImpl implements Inter{ @Override public void show() { System.out.println("show..."); } @Override public void method() { System.out.println("method..."); } }
4、接口中的成员特点
-
成员变量 : 只能定义常量, 因为系统会默认加入三个关键字:public(允许跨包访问) static(通过类名.变量进行访问)final(只能赋值一次,不能更改了,因此变量名要大写)[这三个关键字没有顺序关系]
-
成员方法 : 只能是抽象方法, 因为系统会默认加入两个关键字:public abstract
-
构造方法 : 没有
package com.itheima.minterface; import com.itheima.a.Inter; public class InterfaceTest2 { public static void main(String[] args) { System.out.println(Inter.num); } } interface MyInter { public static final int NUM = 10; public abstract void show(); void method(); }
package com.itheima.a; public interface Inter { static final int num = 10; }
5.类和接口之间的各种关系
①关系
-
类和类的关系:继承关系,只能单继承,但是可以多层继承
-
类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
-
接口和接口的关系:继承关系,可以单继承,也可以多继承
②抽象类和接口的对比
成员变量: 抽象类:可以定义变量,也可以定义常量
接口:只能定义常量
成员方法: 抽象类:可以是定义具体方法,也可以定义抽象方法
接口:只能定义抽象方法
构造方法: 抽象类:有
接口:没有
③抽象类的应用场景
④接口的应用场景
接口可以为程序制定规则,代码更加规范
1.创建接口
2.实现类重写内部抽象方法
⑤总结
抽象类:对事物做抽象(描述事物)
接口:对行为抽象(制定规则)
四、多态
1.介绍
同一个行为(方法)具有多个不同表现形式或形态的能力
OrderService ---> 接口
package com.itheima.order; /** * 订单业务接口 */ public interface OrderService { /** * 查询单个订单 */ void create(); /** * 查询单个订单 */ void findOne(); /** * 查询订单列表 */ void findList(); /** * 取消订单 */ void cancel(); /** * 完结订单 */ void finish(); /** * 支付订单 */ void paid(); }
OrderServiceImpl ---> 国内订单实现类
package com.itheima.order; public class OrderServiceImpl implements OrderService { @Override public void create() { System.out.println("创建订单"); } @Override public void findOne() { System.out.println("查询单个订单"); } @Override public void findList() { System.out.println("查询订单列表"); } @Override public void cancel() { System.out.println("取消订单"); } @Override public void finish() { System.out.println("完结订单"); } @Override public void paid() { System.out.println("支付订单"); } }
OverseasServiceImpl ---> 国外订单实现类
package com.itheima.order; public class OverseasServiceImpl implements OrderService { @Override public void create() { System.out.println("国外业务 --- 创建订单"); } @Override public void findOne() { System.out.println("国外业务 --- 查询单个订单"); } @Override public void findList() { System.out.println("国外业务 --- 查询订单列表"); } @Override public void cancel() { System.out.println("国外业务 --- 取消订单"); } @Override public void finish() { System.out.println("国外业务 --- 完结订单"); } @Override public void paid() { System.out.println("国外业务 --- 支付订单"); } }
测试类
package com.itheima.order; import java.util.Scanner; public class Test { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入: 1. 国内订单 2. 国外订单"); OrderService orderService = null; int choice = sc.nextInt(); switch (choice) { case 1: // 创建国内订单的业务类 orderService = new OrderServiceImpl(); break; case 2: // 创建国外订单的业务类 orderService = new OverseasServiceImpl(); break; } orderService.findOne(); orderService.findList(); orderService.cancel(); orderService.finish(); orderService.paid(); } }
创建对象的时候,左右两边的类型可以不一致,是所有对象都可以这样创建吗?
为什么方法是同一套,但是却有不同的表现形式?(3.②)
2.多态的前提
-
有继承/实现关系
-
有方法重写
-
有父类引用指向子类对象
package polymorphism; public class PolymorphismTest1 { /* 1.有继承/实现关系 2.有方法重写 3.有父类引用指向子类对象 Dog d = new Dog(); ---> 子类引用指向子类对象 对象多态:父类引用指向子类对象: Animal a1 = new Dog(); Animal a2 = new Cat(); 好处:方法的形参可以定义为父类类型,这个方法就可以接收到该父类的任意子类对象了 行为多态: 好处:同一个方法,具有多种不同表现形式或形态的能力 */ public static void main(String[] args) { //对象多态 useAnimal(new Dog()); useAnimal(new Cat()); /* useDog(new Dog()); useCat(new Cat()); */ } /* 优化 useDog 和 useCat public static void useDog(Dog d) { } public static void useCat(Cat d) { } */ //对象多态: public static void useAnimal(Animal a) { //往这个方法中传入不同的对象,就会有不同的表现形式 ---> 行为多态 a.eat(); } } abstract class Animal { public abstract void eat(); } class Dog extends Animal { @Override public void eat() { System.out.println("狗吃肉"); } } class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } }
3.多态的成员访问特点和好处
①访问特点
package com.itheima.polymorphism; public class PolymorphismTest2 { /* 多态的成员访问特点: 1. 成员变量 : 编译看左边(父类), 运行看左边(父类) 2. 成员方法 : 编译看左边(父类), 运行看右边(子类) 在编译的时候, 会检查父类中有没有这个方法 没有 : 编译出错 有 : 编译通过, 但是运行的时候, 一定会执行子类的方法逻辑 原因: 担心你调用的方法, 在父类中是一个抽象方法 --------------------------------------------------------------------- 多态创建对象, 调用静态成员 : 静态的成员, 推荐类名进行调用 细节: 静态的成员, 可以使用对象名调用, 但这是一种假象 - 生成字节码文件后, 会自动将对象名调用, 改成类名调用 */ public static void main(String[] args) { //左边 右边 Fu f = new Zi(); System.out.println(f.num); f.show(); f.print(); // Fu.print(); System.out.println("-----------------"); Inter i = new InterImpl(); i.method(); } } interface Inter { void method(); } class InterImpl implements Inter { @Override public void method() { System.out.println("method..."); } } class Fu { int num = 10; public void show() { System.out.println("Fu...show"); } public static void print(){ System.out.println("Fu...print"); } } class Zi extends Fu { int num = 20; @Override public void show() { System.out.println("Zi...show"); } public static void print(){ System.out.println("Zi...print"); } }
②疑问解答
创建对象的时候,左右两边的类型可以不一致,是所有对象都可以这样创建吗?
不是,必须是父类引用指向子类对象或者接口类引用指向实现类对象
为什么方法是同一套,但是却有不同的表现形式?
在用多态创建完对象调用成员方法的时候,编译检查父类,但运行的时候一定会走子类的代码逻辑,所以不同的子类对象走的是不同的代码逻辑,所以有不同的表现形式
③多态的好处与弊端
多态的好处:提高了程序的扩展性
-
对象多态 :将方法的形参定义为父类类型,这个方法可以接收该父类的任意子类对象
-
行为多态:同一个行为具有多个不同表现形式或形态的能力
多态的弊端:不能使用子类的特有成员(可以使用多态的转型技术)
-
因为在编译的时候, 会检查父类中有没有这个方法,而子类特有的方法父类是一定没有的,会编译出错
4.多态的转型
向下转型需要强制转型
①四、多态-1.--案例补充
OrderService ---> 接口
package com.itheima.order; /** * 订单业务接口 */ public interface OrderService { /** * 查询单个订单 */ void create(); /** * 查询单个订单 */ void findOne(); /** * 查询订单列表 */ void findList(); /** * 取消订单 */ void cancel(); /** * 完结订单 */ void finish(); /** * 支付订单 */ void paid(); }
OrderServiceImpl ---> 国内订单实现类
package com.itheima.order; public class OrderServiceImpl implements OrderService { @Override public void create() { System.out.println("创建订单"); } @Override public void findOne() { System.out.println("查询单个订单"); } @Override public void findList() { System.out.println("查询订单列表"); } @Override public void cancel() { System.out.println("取消订单"); } @Override public void finish() { System.out.println("完结订单"); } @Override public void paid() { System.out.println("支付订单"); } }
OverseasServiceImpl ---> 国外订单实现类
package com.itheima.order; public class OverseasServiceImpl implements OrderService { public void check() { System.out.println("IP地址检测"); } @Override public void create() { System.out.println("国外业务 --- 创建订单"); } @Override public void findOne() { System.out.println("国外业务 --- 查询单个订单"); } @Override public void findList() { System.out.println("国外业务 --- 查询订单列表"); } @Override public void cancel() { System.out.println("国外业务 --- 取消订单"); } @Override public void finish() { System.out.println("国外业务 --- 完结订单"); } @Override public void paid() { System.out.println("国外业务 --- 支付订单"); } }
测试类
package com.itheima.order; import java.util.Scanner; public class Test { /* ClassCastException : 类型转换异常 在引用数据类型的强转中, [实际类型]和[目标类型]不匹配, 就会出现此异常 */ public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入: 1. 国内订单 2. 国外订单"); OrderService orderService = null; int choice = sc.nextInt(); switch (choice) { case 1: // 创建国内订单的业务类 orderService = new OrderServiceImpl(); break; case 2: // 创建国外订单的业务类 orderService = new OverseasServiceImpl(); break; } // instanceof : 判断左边的引用, 是否是右边的数据类型 if(orderService instanceof OverseasServiceImpl){ OverseasServiceImpl osi = (OverseasServiceImpl) orderService; osi.check(); } orderService.create(); orderService.findOne(); orderService.findList(); orderService.cancel(); orderService.finish(); orderService.paid(); } }
②多态中的转型问题
概述:如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
解决方法:
③关键字:instanceof
使用格式:对象名 instanceof 类型
判断一个对象是否是一个类的实例
通俗的理解:判断关键字左边的对象,是否是右边的类型,返回boolean类型结果
④案例
接口
package com.itheima.test; public interface Payment { void pay(double money); }
平台支付
package com.itheima.test; public class PlatformPaymentImpl implements Payment { @Override public void pay(double money) { System.out.println("通过支付平台支付了:" + money + "元!"); } }
银行卡支付
package com.itheima.test; public class BankcardPaymentImpl implements Payment{ @Override public void pay(double money) { System.out.println("通过银行卡网银支付了:" + money + "元!"); } }
信用卡支付
package com.itheima.test; public class CreditCardPaymentImpl implements Payment { @Override public void pay(double money) { System.out.println("通过信用卡快捷支付:" + money + "元!"); } }
测试类
package com.itheima.test; import java.util.Scanner; public class Test { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请选择支付方式: 1. 支付平台支付 2. 银行卡网银支付 3. 信用卡快捷支付"); int choice = sc.nextInt(); Payment payment = null; switch (choice) { case 1 : payment = new PlatformPaymentImpl(); break; case 2: payment = new BankcardPaymentImpl(); break; case 3: payment = new CreditCardPaymentImpl(); break; } System.out.println("请输入您的支付金额: "); double money = sc.nextDouble(); payment.pay(money); } }
五、接口新特性
JDK8
JDK8的新特性:接口中可以定义有方法体的方法。(默认、静态)
JDK9的新特性:接口中可以定义私有方法。
1.介绍
允许在接口中定义非抽象方法,但是需要使用关键字default修饰,这些方法就是默认方法
作用:解决接口升级的问题
接口中允许定义static静态方法
2.接口中默认方法的定义格式:
格式:public default 返回值类型 方法名(参数列表) {}
范例:public default void show() {}
public可以省略,default不可以
3.注意事项
-
默认方法不是抽象方法,所以不强制被重写(但是可以被重写,重写的时候去掉default关键字)
-
public可以省略,default不能省略
-
如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
4.接口中静态方法的定义格式:
格式:public static 返回值类型 方法名(参数列表) {}
范例:public static void show() {}
5.注意事项
-
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
-
public可以省略,static不能省略
package com.itheima.minterface.jdk8; public class InterfaceTest { public static void main(String[] args) { AInterImpl a = new AInterImpl(); a.method(); //静态方法只能通过接口名调用,不能通过实现类名或者对象名调用 A.function(); } } interface A { default void method(){ System.out.println("A...method"); } static void function(){ System.out.println("A...static...function"); } } interface Inter { void show(); void print(); //加入新业务时,为了不改动全局,用default //public可以省略,default不能省略 default void method(){ System.out.println("Inter...method"); } } //Inter和A中方法不一样,强制重写 class AInterImpl extends Object implements Inter , A { @Override public void show() { System.out.println("AInterImpl...show"); } @Override public void print() { System.out.println("AInterImpl...print"); } //默认方法不是抽象方法,所以不强制被重写(但是可以被重写,重写的时候去掉default关键字) @Override public void method() { A.super.method(); Inter.super.method(); } } class BInterImpl implements Inter { @Override public void show() { System.out.println("BInterImpl...show"); } @Override public void print() { System.out.println("BInterImpl...print"); } }
JDK9
1.介绍
接口中允许定义private私有方法(提升复用性,减少冗余代码)
2.接口中静态方法的定义格式
-
格式1:private返回值类型 方法名(参数列表) {}
范例1:private void show() {}
-
格式2:private static返回值类型 方法名(参数列表) {}
范例2:private static void method() {}
package com.itheima.minterface.jdk9; public class InterfaceTest { public static void main(String[] args) { } } interface Inter { void show(); void print(); public static void start(){ System.out.println("start方法执行..."); log(); } public static void end(){ System.out.println("end方法执行..."); log(); } private static void log(){ //重复的逻辑 只为start和end方法提供服务,且不需要暴露给其他方法使用,所以用private System.out.println("日志记录"); } }
六、代码块
1.介绍
代码块 : 使用 { } 括起来的代码被称为代码块
2.分类
①局部代码块(了解)
-
位置:方法中的一对大括号
-
作用: 限定变量的生命周期, 提早的释放内存
②构造代码块(了解)
-
位置: 类中方法外的一对大括号
-
特点: 在创建对象, 每次执行构造方法的时候, 就会执行构造代码块 (优先于构造方法执行)
-
作用: 将多个构造方法中, 重复的代码, 抽取到构造代码块中, 从而提升代码的复用性
③静态代码块(掌握)
-
位置: 类中方法外的一对大括号, 需要加入static关键字
-
特点: 随着类的加载而执行, 因为类只加载一次, 所以也就只执行一次
-
作用: 对数据进行初始化,如果初始化的数据是一个对象且这个对象的初始化很复杂,就可以借助于静态代码块;加载配置文件的时候也会用到
package com.itheima.block; import java.net.Socket; public class BlockTest { public static void main(String[] args) { Student stu1 = new Student(); Student stu2 = new Student(10); } } class Student { static String school; static { //静态代码块,对数据进行初始化, school = "黑马程序员"; System.out.println("static...Student类的静态代码块"); // Socket } //构造代码块,将重复的代码, 抽取到构造代码块中 { System.out.println("Student类的构造代码块"); System.out.println("好好学习"); } public Student(){ System.out.println("空参构造方法..."); } public Student(int num){ System.out.println("带参构造方法..."); } }