5. 继承
优点:
在一定程度上提高代码的复用性(将一些重复的内容不再多次编写)
关键字:
extends
子承父业
将多个类中的共性再一次抽取,作为一个父类。父类的作用就是
用来讲一些重复的内容不再多次编写
注意事项:
java中只支持单继承,一个子类有且只能有一个父类,复用性的提高 是有限的(多继承指的是A继承B,B又继承C)
问题: 多继承好还是单继承好?
单继承: 代码调用复杂度低,复用性有限
多继承: 极大的提高代码复用性,但是代码调用的复杂度也提 升了
假设在应用场景中:
A->B 后期随着业务不断扩展,导致A需要继承C时一般的解决办法:A->B->C
但是该种解决办法随着后期业务的不断升级,导致整个继承连会变得极其复杂,既不利于后期维护以及拓展。能不能用继承就别用继承。
5.1 重写
定义:
也叫方法覆盖,在子类中定义了和父类中同名的方法
为什么?
父类的功能不能满足子类的需求,子类在父类的基础上进行扩展。
如何确定是重写?
在子类方法上加上@Override注解,若不报错,则是重写。
重写前提:
一定要发生继承关系,并且子类的方法名和父类的方法名相同(参数列表,返回类型相同)
5.2 Object
1. 是所有类的根基类,超类
2. 默认没写都是继承Object
3.输出一个对象的时候默认情况会调用当前对象的toString
==比较基本数据类型时,比较的是值,比较引用类型时,比较的是地址
equals 用来比较相等的,如果相等返回true,
4.object中比较是通过==比较的
6. 封装
1. public 公共,都能访问被public修饰的方法、类和变量能被所有 类可见
2. Protected 同包下可见 异包下子类可见
3. 默认的 不写的,同包可见异包不可见
4. private 比较自私自利,本类可见
范围自上而下依次变小
代码:
同包下的两个类(F是父类)
一、
package com.mage.opp.fu;
/**
* 四种修饰符
*/
public class F {
public String name="张三丰"; //public修饰的
// protected String name="张三丰"; //protected修饰的
// private String name="张三丰"; //private修饰
public static void main(String[] args) {
F f = new F();
System.out.println(f.name);
}
}
二、
package com.mage.opp.fu;
public class LaoWang04 {
public static void main(String[] args) {
F f = new F();
System.out.println(f.name);
}
}
不同包下的二个类
一、
package com.mage.opp.fu;
public class LaoWang04 {
public static void main(String[] args) {
F f = new F();
System.out.println(f.name);
}
}
二、
package com.mage.opp.s;
import com.mage.opp.fu.F;
public class LaoWang03 extends F{
public static void main(String[] args) {
F f = new F();
System.out.println("异包子类下的public:"+f.name);
}
}
6.1 修饰变量
局部变量只能通过final修饰
1. 将类中的成员变量通过public、默认的、protected修饰之后导致某情况下可以随意.出来,这些修饰符修饰的变量不够安全。
2. 将这些变量通过private修饰。但是导致无法正常访问。
6.2 修饰方法
1. 修饰符的作用用来屏蔽一些底层的实现逻辑,降低调用者(程序员)的复杂度
2. 确保当前类更加安全,对外调用可以更加简单
3. 属性(成员变量)
避免随意.属性,修改获取属性,造成数据不安全。
如果用private,一定要保证对外提供get、set方法
7.多态
构成多态的前提:
1. 存在继承关系
2. 要有方法的重写
3. 父类变量指向子类变量
代码:{//an.lookDoor();编译会出错[ 编译看左边(指对象an) 运行看右边(指方法lookDoor())]}
package com.mage.opp.duotai;
public class Test01 {
public static void main(String[] args) {
Animal an = new Bird();// 父类变量指向了子类对象
an.shout();
an.eat();
//an.lookDoor();编译会出错[ 编译看左边(指对象an) 运行看右边(指方法lookDoor())]
}
}
class Animal{
public Animal() {
}
public void shout() {
System.out.println("animal shouting!");
}
public void eat() {
System.out.println("animal eat!");
}
}
class Bird extends Animal{
public Bird() {
}
@Override
public void eat() {
System.out.println("重写了 Bird eat");
}
public void lookDoor() {
System.out.println("看门");
}
}
7.1 类的加载顺序
{ }代表代码块:
1. 局部代码块:
声明在方法中的
缩减局部变量的生命周期,提高内存使用率
2. 成员代码块:
声明在方法外,是在类中的代码块
初始代码块在类加载的时候是不会执行的
在创建对象之前会被调用(对于对象中的一些初始进行初始 化操作)
3. 静态代码块:
声明在类中的,方法外,使用static修饰
类加载的时候就会被加载,并且只加载一次 静态内容
执行顺序:
1. 先执行静态内容(加载) 也就是静态代码块
2. 初始化块
3. 构造器
类加载:
1. 当使用当前类中的静态方法、静态变量
2. 当创建当前类的实例对象
代码:
package com.mage.opp.duotai;
public class Test02 {
static{
System.out.println("我是静态代码块");
}
int age;
{
age = 20;
System.out.println("我是初始化块(成员代码块)");
}
public static void main(String[] args) {
{
// int num = 10;
// System.out.println("局部代码块中:"+num);
Test02 t = new Test02();
System.out.println(t.age);
new Test02();
}
}
public Test02() {
System.out.println("我被调用了");
}
}
7.2 类型转换
引用类型也有类型转换:
自动转换:
父类型 变量名 = 子类对象;【new 子类对象|子类对象的变量】
强制转换:
子类型 变量名 = (子类型)父类变量; [事先确定了父类变量中实际存储的对象是什么类型]
ClassCastException 类型转换异常
7.3 final
* final修饰的变量称之为最终常量,在程序运行期间其值不可发生改变
* final修饰的基本数据类型变量,无法进行修改,修饰引用类型时只是地址不会改变,对象中的内容还是会变化的(如下代码)
* final修饰的类不可以被继承(可以加太监类【无子类】)
* final修饰的方法不可以被重写
7.3.1 final面试题
final修饰静态常量经过方法
package com.mage.fina;
public class Test {
//final static int num = 10;
final static Bird bird = new Bird("麻雀","棕色");
public static void main(String[] args) {
/* fun(num);
System.out.println("main方法中"+num);
*/
change();
}
public static void change() {
bird.setColor("花色");
//bird = new Bird(); 修饰的引用类型的变量 地址不变所以无法重新new同一个对象
System.out.println("change中"+bird);
}
/*public static void fun(int num) {
num++;
System.out.println("fun方法中"+num);
}
*/
}
class Bird{
private String name;
private String color;
public Bird() {
}
public Bird(String name,String color) {
super();
this.name = name;
this.color = color;
}
@Override
public String toString() {
return "Bird [name=" + name + ", color=" + color + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
final修饰的静态常量不会导致类加载
1. 静态成员常量不会导致类加载
2. 静态成员常量的值在加载前是无法确定的,就会导致类加载
两个类同时用,有联系的
一、
package com.mage.fina;
public class Test01 {
//final static int num = 10;
final static int num = (int)(Math.random()*12);
static {
System.out.println("我是静态代码块");
}
public static void main(String[] args) {
System.out.println(Test01.num);
}
}
二、
package com.mage.fina;
public class Test011 {
public static void main(String[] args) {
System.out.println(Test01.num);
}
}
8. abstract抽象类
1. 父类中定义的方法不需要具体的实现步骤,子类都不按照父类的做
2. 父类中定义的方法目的是要告诉子类一定要实现该类中的方法(也就是重写父类中的方法)
定义:
包含了抽象方法的类称为抽象类
被abstract修饰的类称为抽象类
抽象方法:
只要方法的声明,没有方法体,通过abstract修饰的方法称之为抽象方法
形式:
abstract class Person{
public abstract void eat() ;
}
为什么需要抽象类?
避免子类的随意设计 提高代码可读性,提高了子类的健壮性
abstract class Person{
public abstract void eat() ;
}
class Chinese extends Person{
public void eat() {
}
}
class En{
public void eat() {
}
}
深入了解抽象类
1. 抽象类中只能有抽象方法吗
不是,既可以定义抽象方法也可以普通方法
2.是否可以定义构造器呢
可以,存在构造器但无法实例化,构造器只是给子类用的。
3.抽象类就是用来被继承的,抽象方法就是被用来重写的,继承之后必须 重写所有的抽象方法。
package com.mage.opp.abstra;
public class Test03 {
public static void main(String[] args) {
//Animal an = new Animal(); 无法进行实例化所以报错
Bird bird = new Bird();
bird.eat();
}
}
abstract class Animal{
public Animal() {
System.out.println("我是抽象类的构造器");
}
public abstract void eat();
}
class Bird extends Animal{
public Bird() {
}
public void eat() {
System.out.println("我重写了抽象类中的方法");
}
}
8.1 模板方法
模板方法:
1. 将上下文中的一些不会变化的内容保留下来 在父类中实现
2. 并且定义整个程序的执行流程
3. 将核心的业务逻辑或者是算法逻辑延迟到子类中去实现。
喝茶 烧水 冲泡 干了
喝咖啡 烧水 搅拌 干了
package com.mage.oop.abstrac;
public class Test03 {
public static void main(String[] args) {
/*
* Tea t = new Tea(); t.flow();
*
* Caf c = new Caf(); c.flow();
*/
Water w1 = new Tea();
w1.flow();
Water w2 = new Caf();
w2.flow();
}
}
abstract class Water{
private void fireWater() {
System.out.println("咕噜咕噜咕噜");
}
public abstract void pp() ;
private void drink() {
System.out.println("墩儿~墩儿~墩儿~墩儿~");
}
public void flow() {
fireWater();
pp();
drink();
}
}
class Tea extends Water{
public void pp() {
System.out.println("冲泡");
}
}
class Caf extends Water{
public void pp() {
System.out.println("搅拌");
}
}
9. 接口
接口是一个规范 是一套标准 比抽象类还要抽象
Tips:
接口就是一套规则,用来定义具体要做哪些事情,但是所有事情的具体实 现都会延迟到实现类中完成。接口只
需要定义has-a的关系,如果你是什么,则你具备了什么能力。
学习接口和学习类是一样的
形式:
修饰符 interface 接口名{}
1. 接口中的变量是公开的(要被其他类使用) 静态的最终常量值 默认情况下变量都是public static final修饰的,如下代码
int SPEED = 10;默认是public final static int SPEED = 10;
2.接口中可以定义静态方法(1.8版本后不建议用)
3. 接口中定义的对象方法都是抽象方法(因为比抽象类还要抽象),接口中的方法默认就是通过abstract修饰的
4.接口中默认方法从1.8之后才开始使用,允许在接口中定义default方法,而且存在方法体
interface A{
int SPEED = 10;
default void method() {
}
public static void fun1() {
}
public abstract void fun2() ;
}
9.1 深入了解接口
形式:
修饰符 class 类名 extends 父类 implements 接口
1. 类和接口直接通过implements发生关系,类实现接口
2. 类必须实现接口中所有抽象方法
3. 可以实现多个接口,类名 implements 接口1,接口2...
4. 一个类实现了接口之后,要将当前接口及接口的父接口中的所有抽象方法全部重写
5. 接口可以多继承
6. 接口无法实例化
7. 接口没有构造器
8. 接口中也可以使用多态
equals
equals方法就是用来比较两个对象是否相等的,默认Object的equals方法比较是两个对象的地址。
java.lang.NullPointerException 空指针异常 对象为null
ClassCastException 类型转换异常
null可以强转为任意类型 null也可以是任意类型