Java基础学习第八天——类的继承与初始化

文档版本开发工具测试平台工程名字日期作者备注
V1.02016.02.29lutianfeinone


  • 问题:什么默认初始化,什么是显示初始化?

工具类中使用静态方法的常见问题

  • 当报错无法从静态上下文中引用非静态 方法xxx
    • 表明在静态方法中引用了非静态方法
  • 在工具类中当把构造方法私有后,外界就不能再创建对象了,此时将所有方法设为静态后。可以根据静态方法的特点:通过类名来调用

  • Eg:

class ArrayDemo {
    public static void main(String[] args) {
        //定义数组
        int[] arr = {28,55,37,46,19};

        //非静态方法
        //ArrayDemo ad = new ArrayDemo();
        //ad.printArray(arr);

        //方法改进为静态后,就可以直接通过类名调用
        ArrayTool.printArray(arr);
    }
}


class ArrayTool {

    //把构造方法私有,外界就不能在创建对象了
    private ArrayTool(){}

    public static void printArray(int[] arr) {
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1) {
                System.out.println(arr[x]);
            }else {
                System.out.print(arr[x]+", ");
            }
        }
    }
}


如何使用帮助文档

  • 点击显示,找到索引,出现输入框
  • 你应该知道你找谁?举例:Scanner
  • 看这个类的结构(需不需要导包) 注:java.lang包下的类不需要导入,其他的全部需要导入。
    • 成员变量 说明书中对照为字段摘要
    • 构造方法 说明书中对照为构造方法摘要
      • 看到有构造方法 : 创建对象
      • 没有看到构造方法:成员可能都是静态的,通过类名调用
    • 成员方法 说明书中对照为方法摘要
      • 左边
        • 是否静态:如果静态,可以通过类名调用
        • 返回值类型:定义返回的是说明,就用什么接收。
      • 右边
        • 方法名,不要打错
        • 参数列表:需要什么参数,就给什么,需要几个给几个
    • 版本说明


Math类学习

  • Math:类包含用于执行基本数学运算的方法

  • 由于Math类在java.lang包下,所以不需要导包。

  • 特点:

    • 没有构造方法,因为它的成员全部是静态的。
  • 常见方法: 参见手册

    • 获取随机数
      • public static double random():返回带正号double 值,该值大于等于 0.0 且小于 1.0
  • 获取一个1-100之间的随机数

class MathDemo {
    public static void main(String[] args) {
        //获取一个随机数
        //double d = Math.random();
        //System.out.println(d);
        for(int x=0; x<100; x++) {
            int number = (int)(Math.random()*100)+1;
            System.out.println(number);
        }
    }
}


代码块

  • 在Java中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块构造代码块静态代码块同步代码块(多线程讲解)。
  • 局部代码块
    • 方法中出现;限定变量生命周期,及早释放,提高内存利用率
  • 构造代码块
    • 类中,方法外出现;
    • 多个构造方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法执行。
      • 作用:可以把多个构造方法中的共同代码放到一起。
  • 静态代码块

    • 类中,方法外出现,加了static修饰;用于给进行初始化,在类加载的时候就执行,并且只执行一次
  • eg1 : 关于代码块的经典面试题

class Code {
    static { //静态代码块
        int a = 1000;
        System.out.println(a);
    }

    //构造代码块
    {
        int x = 100;
        System.out.println(x);
    }

    //构造方法
    public Code(){
        System.out.println("code");
    }

    //构造方法
    public Code(int a){
        System.out.println("code");
    }

    //构造代码块
    {
        int y = 200;
        System.out.println(y);
    }

    //静态代码块
    static {
        int b = 2000;
        System.out.println(b);
    }
}

class CodeDemo {
    public static void main(String[] args) {
        //局部代码块
        {
            int x = 10;
            System.out.println(x);
        }
        //找不到符号
        //System.out.println(x);
        {
            int y = 20;
            System.out.println(y);
        }
        System.out.println("---------------");

        Code c = new Code();    
        System.out.println("---------------");
        Code c2 = new Code();
        System.out.println("---------------");
        Code c3 = new Code(1);
    }
}


/*
* 程序运行结果:
10
20
---------------
1000
2000
100
200
code
---------------
100
200
code
---------------
100
200
code
*/


  • Eg2 :
class Student {
    static {
        System.out.println("Student 静态代码块");
    }

    {
        System.out.println("Student 构造代码块");
    }

    public Student() {
        System.out.println("Student 构造方法");
    }
}

class StudentDemo {
    static {
        System.out.println("林青霞都60了,我很伤心");
    }

    public static void main(String[] args) {
        System.out.println("我是main方法");

        Student s1 = new Student();
        Student s2 = new Student();
    }
}

/*
    写程序的执行结果。

    林青霞都60了,我很伤心
    我是main方法
    Student 静态代码块
    Student 构造代码块
    Student 构造方法
    Student 构造代码块
    Student 构造方法
*/


继承

继承概述
  • 多个类中存在相同属性行为时,将这些内容抽取到单独一个类中,那么其他类无需再定义这些属性和行为,只要继承这个类。
  • 通过extends关键字可以实现类与类的继承
    • 格式: class 子类名 extends 父类名 {}
  • 单独的这个类称为父类基类或者超类;这多个类可以称为子类或者派生类
  • 有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
class Dad(){}
class Son extends Dad {}


继承的好处
  • 提高了代码的复用性
    • 多个类相同的成员可以放到同一个类中
  • 提高了代码的维护性
    • 如果功能的代码需要修改,修改一处即可
  • 让类与类之间产生了关系,是多态的前提

    • 其实这也是继承的一个弊端:类的耦合性很强
  • 开发的原则:低耦合,高内聚

    • 耦合:类与类的关系。
    • 内聚:就是自己完成某件事情的能力。
Java继承的特点
  • Java只支持单继承,不支持多继承
    • 一个类只能有一个父类,不可以有多个父类。
      • class SubDemo extends Demo{} //ok
      • class SubDemo extends Demo1,Demo2…//error
  • Java支持多层继承(继承体系)
    • class A{}
    • class B extends A{}
    • class C extends B{}
Java中继承的注意事项
  • 子类只能继承父类所有非私有的成员(成员方法和成员变量)
    • 其实这也体现了继承的另一个弊端:打破了封装性
  • 子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
  • 不要为了部分功能而去继承

  • Eg1:

class Father {
    private int num = 10;
    public int num2 = 20;

    //私有方法,子类不能继承
    private void method() {
        System.out.println(num);
        System.out.println(num2);
    }

    public void show() {
        System.out.println(num);
        System.out.println(num2);
    }
}

class Son extends Father {
    public void function() {
        //num可以在Father中访问
        //System.out.println(num); //子类不能继承父类的私有成员变量
        System.out.println(num2);
    }
}

class ExtendsDemo3 {
    public static void main(String[] args) {
        // 创建对象
        Son s = new Son();
        //s.method(); //子类不能继承父类的私有成员方法
        s.show();
        s.function();
    }
}


什么时候使用继承
  • 继承中类之间体现的是:”is a”的关系。
  • 假设法:如果有两个类A,B。只有他们符合A是B的一种,或者B是A的一种,就可以考虑使用继承。
继承中成员变量的关系
  • 在子类方法中访问一个变量
  • 首先在子类局部范围
  • 然后在子类成员范围
  • 最后在父类成员范围找(肯定不能访问到父类局部范围)
  • 如果还是没有就报错。(不考虑父亲的父亲…)
super关键字
  • super的用法和this很像
    • this代表本类对应的引用。
    • super代表父类存储空间的标识(可以理解为父类引用)
  • 用法
    • 访问成员变量
      • this.成员变量
      • super.成员变量
    • 访问构造方法
      • this(…) :调用本类构造方法
      • super(…):调用父类构造方法
    • 访问成员方法
      • this.成员方法()
      • super.成员方法()
继承中构造方法的关系
  • 子类中所有的构造方法默认都会访问父类中的无参构造方法
    • 因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化
    • 每一个构造方法的第一条语句默认都是:super()
class Father {
    int age;

    public Father() {
        System.out.println("Father的无参构造方法");
    }

    public Father(String name) {
        System.out.println("Father的带参构造方法");
    }
}

class Son extends Father {
    public Son() {
        //super();//默认都会有的
        System.out.println("Son的无参构造方法");
    }

    public Son(String name) {
        //super();//默认都会有的
        System.out.println("Son的带参构造方法");
    }
}    

class ExtendsDemo6 {
    public static void main(String[] args) {
        //创建对象
        Son s = new Son();
        System.out.println("------------");
        Son s2 = new Son("林青霞");
    }
}

//程序运行结果
/*
Father的无参构造方法
Son的无参构造方法
------------------------
Father的无参构造方法
Son的带参构造方法
*/


  • 如果父类没有无参构造方法,那么子类的构造方法会出现什么现象呢,如何解决?
    • 会报错
    • 解决办法:
      • A:在父类中加一个无参构造方法
      • B:通过使用super关键字去显式的的调用父类的带参构造方法
    • 子类中一定要有一个方法访问了父类的构造方法,否则父类数据就没有办法初始化。
  • 注:super(…)必须放在第一条语句上,否则就可能出现对父类的数据进行了多次初始化。


关于初始化顺序的整理
  • A:一个类的静态代码块,构造代码块,构造方法的执行流程
    • 静态代码块 > 构造代码块 > 构造方法
  • B:静态的内容是随着类的加载而加载
    • 静态代码块的内容会优先执行
  • C:子类初始化之前先会进行父类的初始化

  • D:同一个类中的初始化过程

    • 成员变量进行初始化
    • 默认初始化
    • 显式初始化
    • 构造方法初始化


  • 练习1:
class Fu{
    public int num = 10;
    public Fu(){
        System.out.println("fu");
    }
}
class Zi extends Fu{
    public int num = 20;
    public Zi(){
        System.out.println("zi");
    }
    public void show(){
        int num = 30;
        System.out.println(num); //30
        System.out.println(this.num); //20
        System.out.println(super.num); //10
    }
}
class ExtendsTest {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

/*
    结果:
        fu
        zi
        30
        20
        10
*/


  • 练习2:
class Fu {
    static {
        System.out.println("静态代码块Fu");
    }

    {
        System.out.println("构造代码块Fu");
    }

    public Fu() {
        System.out.println("构造方法Fu");
    }
}

class Zi extends Fu {
    static {
        System.out.println("静态代码块Zi");
    }

    {
        System.out.println("构造代码块Zi");
    }

    public Zi() {
        System.out.println("构造方法Zi");
    }
}

class ExtendsTest2 {
    public static void main(String[] args) {
        Zi z = new Zi();
    }
}

/*
    结果是:
        静态代码块Fu
        静态代码块Zi
        构造代码块Fu
        构造方法Fu
        构造代码块Zi
        构造方法Zi
*/


  • 练习3(难题)
class X {
    Y b = new Y();
    X() {
        System.out.print("X");
    }
}

class Y {
    Y() {
        System.out.print("Y");
    }
}

public class Z extends X {
    Y y = new Y();
    Z() {
        //super
        System.out.print("Z");
    }
    public static void main(String[] args) {
        new Z(); 
    }
}

/*
    结果:
        YXYZ
    分析:
    step1:在对Z初始化之前先对其父类X初始化:X初始化化时先对成员变量初始化及对Y对象初始化,打印Y
    step2:再对构造方法初始化,打印X
    step3: 接着对子类初始化,先对成员变量初始化,打印Y
    step4:最后对Z构造方法初始化打印Z
*/


继承中成员方法的关系

方法重写(Override)
  • 子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖方法复写方法重写
  • 使用特点:
    • 如果方法名不同,就调用对应的方法
    • 如果方法名相同,最终使用的是子类自己
  • 方法重写的应用:
    • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
方法重写的注意事项
  • 父类中私有方法不能被重写
  • 子类重写父类方法时,访问权限不能更低
  • 父类静态方法,子类也必须通过静态方法进行重写。(其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中会讲解)
class Phone {
    public void call(String name) {
        System.out.println("给"+name+"打电话");
    }
}

class NewPhone extends Phone {
    public void call(String name) {
        //System.out.println("给"+name+"打电话");
        super.call(name);
        System.out.println("可以听天气预报了");
    }
}

class ExtendsDemo9 {
    public static void main(String[] args) {
        NewPhone np = new NewPhone();
        np.call("林青霞");
    }
}


练习题

  • Eg1:方法重写方法重载的区别?方法重载能改变返回值类型吗?

    • 方法重写(Override)
      • 子类中,出现和父类中一模一样方法声明的现象。
    • 方法重载(Overload)
      • 同一个类中,出现的方法名相同参数列表不同的现象。
    • 方法重载能改变返回值类型,因为它和返回值类型无关。
  • Eg2: this关键字和super关键字分别代表什么?以及他们各自的使用场景和作用。

    • this:代表当前类的对象引用
    • super:代表父类存储空间的标识。(可以理解为父类的引用,通过这个东西可以访问父类的成员)
    • 场景:
      • 成员变量:
        • this.成员变量
        • super.成员变量
      • 构造方法:
        • this(…)
        • super(…)
      • 成员方法:
        • this.成员方法
        • super.成员方法
  • Eg3:

//定义人类
class Person {
    //姓名
    private String name;
    //年龄
    private int age;

    public Person() {
    }

    public Person(String name,int age) { //"林青霞",27
        this.name = name;
        this.age = age;
    }

    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;
    }
}

//定义学生类
class Student extends Person {
    public Student() {}

    public Student(String name,int age) { //"林青霞",27
        //this.name = name;
        //this.age = age;
        super(name,age);
    }
}

class ExtendsTest4 {
    public static void main(String[] args) {
        //创建学生对象并测试
        //方式1
        Student s1 = new Student();
        s1.setName("林青霞");
        s1.setAge(27);
        System.out.println(s1.getName()+"---"+s1.getAge());

        //方式2
        Student s2 = new Student("林青霞",27);
        System.out.println(s2.getName()+"---"+s2.getAge());

    }
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值