day09
一、抽象类
1.1 格式
关键字 abstract
权限修饰符 abstract class 类名 {
}
public abstract class Person {
}
1.2 作用
1. 提取一些类共有的内容到抽象类中
2. 主要用来当父类
1.3 特点
1. 抽象类中可以放抽象方法的。
2. 抽象方法一定要放在抽象类中。
3. 抽象类不能实例化(也就是不能创建对象)
抽象类虽然不能创建对象,但是它的子类可以创建对象
抽象方法的格式:
权限修饰符 abstract 返回值类型 方法名(参数列表);
1.4 抽象类中的成员
成员变量:
普通的成员变量、静态的变量、常量
成员方法:
普通的成员方法、抽象方法
构造方法:
有构造方法(但是不能用来创建对象,主要是用来对抽象类中的成员进行初始化,方便子类使用)
package com.ujiuye.demo01_抽象类;
/*
抽象的人类
*/
public abstract class Person {
String name;
int age;
static String sex;
final String country = "中国";
//抽象方法
public abstract void eat();
public abstract void sleep();
//普通的成员方法
public void playGame(){
}
}
1.5 如何处理抽象类中的抽象方法
1. 将类声明为抽象类 【没有意义,所以不建议】
2. 将父类中的抽象方法全部重写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GUxwRZAs-1620291107393)(img/image-20210416113957371.png)]
/*
方式一:声明为抽象
*/
public abstract class Nurse extends Person {
}
/*
方式二:全部重写
*/
public class Nurse extends Person {
@Override
public void eat() {
System.out.println("一个小护士喜欢吃饭...");
}
@Override
public void sleep() {
System.out.println("躺着睡觉");
}
}
员工类练习:用继承、抽象类、抽象方法进行改进。
程序员类: 属性(姓名、工号、工资、奖金) 行为(工作:软件开发)
测试工程师类: 属性(姓名、工号、工资) 行为(工作:软件测试)
项目经理类: 属性(姓名、工号、工资、奖金) 行为(工作:控制进度)
package com.ujiuye.demo01_抽象类;
/*
抽象员工类
*/
public abstract class Worker {
String name;
int id;
int salary;
public abstract void work();
public void show(){
System.out.println(name + ", " + id + ", " + salary);
}
}
/*
Java开发工程师
*/
public class JavaWorker extends Worker {
int bonus;
/*
生成构造方法的快捷键: alt + insert
*/
public JavaWorker() {
}
public JavaWorker(String name, int id, int salary, int bonus) {
this.name = name;
this.id = id;
this.salary = salary;
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("软件开发");
}
@Override
public void show() {
super.show();
System.out.println(bonus);
}
}
/*
测试工程师类
*/
public class TestWorker extends Worker {
public TestWorker() {
}
public TestWorker(String name, int id, int salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
@Override
public void work() {
System.out.println("软件测试");
}
@Override
public void show() {
super.show();
}
}
/*
项目经理类
*/
public class PMWorker extends Worker {
int bonus;
public PMWorker() {
}
public PMWorker(String name, int id, int salary, int bonus) {
this.name = name;
this.id = id;
this.salary = salary;
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("控制进度");
}
@Override
public void show() {
super.show();
System.out.println(bonus);
}
}
/*
测试类
*/
public class Demo2 {
public static void main(String[] args) {
JavaWorker jw = new JavaWorker();
JavaWorker jw2 = new JavaWorker("雷军", 1, 1, 10000000);
jw.work();
jw2.show();
PMWorker pw = new PMWorker();
pw.work();
}
}
二、final关键字
final可以修饰类、方法、变量
修饰类:
类不能被继承
修饰方法:
方法不能被重写
修饰变量:
变量变为了常量(常量就是在程序运行过程中不会发生改变的量)
注意:
1. 常量在创建对象之前必须要完成初始化,否则报错
1. 直接显示初始化
2. 在构造方法中初始化
3. 在构造代码块中初始化
2. 一般我们声明常量的格式为:public static final
public class Person {
/*
因为常量不能在程序运行过程中发生改变,所以常量一般都是记录一些固定的值,比如:圆周率PI,int类型的最大值MAX_VALUE
一般我们定义常量的时候为了方便访问:都会默认写为 public static final 数据类型 变量名 = 常量值;
*/
public static final int I = 10;
}
三、接口
接口也是Java中的一种引用数据类型。Java接口是一个能装方法的容器(并且这些方法一般都是抽象方法)。
接口相当于定义了一组规范。实现这个接口的类都要实现接口中的方法。
3.1 格式
关键字 interface
权限修饰符 interface 接口名 {
}
注意:接口也和类一样,可以编译生成一个class文件
3.2 特点
1. 接口不能实例化
2. 接口中没有构造方法(所以接口不能创建对象)
那么接口如何创建对象?
可以通过接口的实现类(也可以叫做接口的子类)创建对象
关键字 implements
interface 接口名 {
}
class 实现类 implements 接口名 {
}
/*
接口
*/
public interface A {
public abstract void test();
}
/*
实现类
*/
public class AImpl implements A {
@Override
public void test() {
System.out.println("重写后的内容...");
}
}
/*
测试类
*/
public class Demo {
public static void main(String[] args) {
// A a = new A();
AImpl a = new AImpl();
a.test();
}
}
3.3 接口中的成员
成员变量:
接口中的只有常量,默认被public static final修饰
成员方法:
1. 接口中的方法默认是抽象方法。默认被public abstract修饰
2. 被default修饰的方法 【jdk8新增】
3. 被static修饰的方法 【jdk8新增】
只能通过 接口名.静态方法名 调用
4. 被private修饰的方法 【jdk9新增】
构造方法:
接口中没有构造方法
为什么接口中不需要构造方法?
因为接口中方法默认都是抽象的,接口中只有常量,这些都不需要进行初始化就可以使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T05sNhK2-1620291107397)(img/image-20210416140447770.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q1nAQjR1-1620291107398)(img/image-20210416140648330.png)]
3.4 类与类、类与接口、接口与接口之间的关系
类与类
1. 可以单继承
class Fu {}
class Zi extends Fu {}
2. 不可以多继承
class Fu {}
class Mu {}
class Zi extends Fu, Mu {} //错误
3. 可以多层继承(继承体系)
class Ye {}
class Fu extends Ye {}
class Zi extends Fu {}
类与接口
1. 可以单实现
interface A {}
class AImpl implements A {}
2. 可以多实现
interface A {}
interface B {}
class C implements A, B {}
注意:一个类可以先继承一个类,然后再多实现接口
如:
class A {}
interface B {}
interface C {}
class D extends A implements B, C {}
接口与接口
1. 可以单继承
interface A {}
interface B extends A {}
2. 可以多继承
interface A {}
interface B {}
interface C extends A, B {}
练习:
1. 定义一个抽象动物类 Animal
属性:姓名、年龄
行为:吃饭、睡觉 (抽象方法)
2. 定义一个AnimalTrain接口
行为:开车
3. 定义一个猫类继承Animal类
4. 定义一个狗类继承Animal类
5. 定义一个超狗类继承Animal,并且会开车(也就是实现接口)
6. 在测试中创建猫类、狗类、超狗类并进行测试
package com.ujiuye.demo03_接口;
/*
抽象动物类Animal
*/
abstract class Animal {
String name;
int age;
public abstract void eat();
public abstract void sleep();
}
/*
AnimalTrain接口
*/
interface AnimalTrain {
public abstract void driveCar();
}
/*
猫类
*/
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void sleep() {
System.out.println("猫趴着睡觉");
}
}
/*
狗类
*/
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
@Override
public void sleep() {
System.out.println("狗斜着身子睡觉");
}
}
/*
超狗
*/
class SuperDog extends Animal implements AnimalTrain {
@Override
public void eat() {
System.out.println("超狗吃海鲜");
}
@Override
public void sleep() {
System.out.println("超狗睡在100平米的大床上");
}
@Override
public void driveCar() {
System.out.println("超狗会开车");
}
}
public class Demo2 {
public static void main(String[] args) {
Cat c = new Cat();
c.eat();
c.sleep();
System.out.println("---------");
Dog d = new Dog();
d.eat();
d.sleep();
System.out.println("---------");
SuperDog sd = new SuperDog();
sd.eat();
sd.sleep();
sd.driveCar();
}
}
四、多态
面向对象三大特征:封装、继承、多态
生活中:比如跑,猫、狗、狮子,跑的形态是不一样的。比如飞:昆虫、鸟类、飞机,飞的形态是不一样的。以上这些,都是对于同一个行为,不同的事物有不同的表示形态。这些就是多态的体现。
4.1 概念
生活中多态: 对于同一个行为,不同的事物有不同的表示形态
Java中的多态:对于同一个方法,不同的对象有不同的实现方式(实际上就是重写了)
4.2 前提
1. 有父类继承或者接口实现
2. 子类重写父类的方法 【如果方法不重写,那么就谈不上多态】
3. 父类引用指向子类对象 Fu f = new Zi();
package com.ujiuye.demo04_多态;
class Fu {
public void play(){
}
}
//1. 有父类继承或者接口实现
class Zi extends Fu {
//2. 有方法的重写
@Override
public void play() {
System.out.println("小男孩喜欢玩游戏");
}
}
public class Demo {
public static void main(String[] args) {
//3. 父类引用指向子类对象
Fu f = new Zi();
}
}
4.3 多态中访问成员的特点
父类的类型 父类的引用 = new 子类的类型();
等号的左边:就是父类的引用
等号的右边:就是子类的类型
注意观察结果:1. 编译错不错 2. 结果是父类的还是子类的
访问成员变量的时候:
编译的时候看的是左边(如果父类中有就不报错),运行的时候看的也是左边(结果也是父类中的内容)
访问成员方法的时候:
编译的时候看的是左边(如果父类中有就不报错),运行的时候看的是右边(结果是子类中重写之后的内容)
package com.ujiuye.demo04_多态;
class A {
int i = 10;
int j = 30;
public void test() {
System.out.println("A...test");
}
public void aa(){
System.out.println("A...aa");
}
}
class B extends A {
int i = 20;
int k = 40;
public void test() {
System.out.println("B...test");
}
public void bb(){
System.out.println("B...bb");
}
}
public class Demo2 {
public static void main(String[] args) {
A a = new B();
// System.out.println(a.i); //10 //父类和子类都有,编译不报错,结果的是父类的
// System.out.println(a.j); //30 //父类中有,子类中没有,编译不报错,结果的是父类的
// System.out.println(a.k); //编译报错。父类中没有,子类中有
// a.test(); //B...test //父类和子类都有,编译不报错,结果是子类的
// a.aa(); //A...aa //父类中有,子类中没有,编译不报错,结果是父类的
// a.bb(); //编译报错。 父类中没有,子类中有
}
}
4.4 多态的好处
1. 提高了代码的可维护性 (由继承和实现保证)
2. 提高了代码的可扩展性 (由多态保证)
package com.ujiuye.demo04_多态;
abstract class Animal {
String name;
int age;
public abstract void eat();
public abstract void sleep();
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鸡肉");
}
@Override
public void sleep() {
System.out.println("猫趴着睡觉");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
@Override
public void sleep() {
System.out.println("狗趴着睡觉");
}
}
class Snake extends Animal {
@Override
public void eat() {
System.out.println("蛇吃老鼠");
}
@Override
public void sleep() {
System.out.println("蛇盘着睡觉");
}
}
public class Demo3 {
public static void main(String[] args) {
Cat c = new Cat();
// c.eat();
// c.sleep();
// feedCat(c);
feed(c);
System.out.println("----------");
Cat c2 = new Cat();
// c2.eat();
// c2.sleep();
// feedCat(c2);
feed(c2);
System.out.println("----------");
Dog d = new Dog();
// d.eat();
// d.sleep();
// feedDog(d);
feed(d);
System.out.println("----------");
Dog d2 = new Dog();
// d2.eat();
// d2.sleep();
// feedDog(d2);
feed(d2);
System.out.println("----------");
Snake s = new Snake();
// feedSnake(s);
feed(s);
System.out.println("----------");
Snake s2 = new Snake();
// feedSnake(s2);
feed(s2);
}
/*
养动物的方法
*/
public static void feed(Animal a){ //Animal a = new Cat(); //Animal a = new Dog();
a.eat();
a.sleep();
}
/*
养猫的方法
*/
// public static void feedCat(Cat c){ //需要的是类这种类型,实际上需要的是该类的对象 Cat c = new Cat();
// c.eat();
// c.sleep();
// //...7
// }
/*
养狗的方法
*/
// public static void feedDog(Dog d){
// d.eat();
// d.sleep();
// }
/*
养蛇的方法
*/
// public static void feedSnake(Snake s){
// s.eat();
// s.sleep();
// }
}
4.5 多态的弊端
多态中,不能访问子类特有的功能?
如何处理?
1. 创建子类的对象,通过子类的对象访问
2. 将父类的引用再转为子类的引用即可(因为父类的引用本来就是指向了子类的对象,所以可以转换) (向下转型)
4.6 向下转型及向上转型
向上转型
向上转型:由小的类型到大的类型
Fu f = new Zi(); //从小的类型Zi到大的类型Fu
向下转型
向下转型:由大的类型到小的类型(这种转换是不安全,所以需要强制执行)
较小的数据类型 对象名 = (较小的数据类型)较大的数据类型;
如何安全的转换呢?
有一个关键字instanceof可以判断一个对象是否属于某一种类型
格式:
对象 instanceof 类型
如果对象属于这种类型,那么返回true
如果不属于那么返回false
package com.ujiuye.demo04_多态;
class Person {
public void test(){
System.out.println("Person...test");
}
}
class Student extends Person {
public void test(){
System.out.println("Student...test");
}
public void show(){
System.out.println("Student...show");
}
}
public class Demo4 {
public static void main(String[] args) {
Person p = new Student();
// p.test();
// p.show(); //编译报错
//方式一:创建子类的对象,通过子类的对象访问
// Student s = new Student();
// s.show();
//方式二:向下转型 较小的数据类型 对象名 = (较小的数据类型)较大的数据类型;
Student s = (Student)p;
s.show();
// Student s = new Student();
// System.out.println(s instanceof Person);
}
}
思考:
1. 多态用在参数中 (参考上方的Animal类)
2. 多态用在返回值中
ge com.ujiuye.demo04_多态;
class Person {
public void test(){
System.out.println("Person...test");
}
}
class Student extends Person {
public void test(){
System.out.println("Student...test");
}
public void show(){
System.out.println("Student...show");
}
}
public class Demo4 {
public static void main(String[] args) {
Person p = new Student();
// p.test();
// p.show(); //编译报错
//方式一:创建子类的对象,通过子类的对象访问
// Student s = new Student();
// s.show();
//方式二:向下转型 较小的数据类型 对象名 = (较小的数据类型)较大的数据类型;
Student s = (Student)p;
s.show();
// Student s = new Student();
// System.out.println(s instanceof Person);
}
}
思考:
1. 多态用在参数中 (参考上方的Animal类)
2. 多态用在返回值中