JAVASE的学习笔记(三)(继承与多态,final,super,stati与单例模式)

JAVASE的学习笔记(三)

继承

作用:

  • 复用性
  • 扩展性

A.一个Java类只能出现一次直接的父类

B.一个Java类可以由多个间接的父类

子类继承了什么

  1. 子类“完完全全”继承了父类中的属性和方法,不管使用什么访问修饰符,没有继承的是父类的构造方法
  2. 子类无法“直接”操作使用private修饰的属性和方法,private修饰只能在父类范围中使用
  3. 子类可以“间接”操作private修饰的属性和方法,但是需要父类提供可以被继承的方法操作

java.lang.Object类,每个class都有Object作为超类

子类重写父类中的方法,需要满足以下条件
  • A.方法名称必须相同
  • B.形参列表必须相同(个数和类型)
  • C.返回值类型必须相同,基本数据类型
    • 引用数据类型,子类中的类型需要小于父类中的类型
重写方法特点
  1. 方法的重写也可以叫做方法的覆盖
  2. 方法的重写:必须具有类和类的继承关系
  3. 子类重写父类中的方法,需要满足以下条件
       A.方法名称必须相同
       B.形参列表必须相同(个数和类型)
       C.返回值类型必须相同,基本数据类型
          引用数据类型,子类中的类型需要小于父类中的类型
  4. 子类重写方法的访问修饰符必须大于等于父类的访问修饰符,private除外
  5. 子类无法重写父类中的构造方法,原因:子类无法继承父类中构造方法的
  6. 重写方法是针对于父类可以被继承`的 实例方法
  7. 子类无法重写父类中使用private修饰的方法
  8. 子类无法重写父类中使用static修饰的静态方法(类方法)
  9. 子类抛出的异常需要小于等于父类中抛出的异常

final关键字

  • 最终,不可变
  • 修饰类,无法被继承
  • 修饰方法,无法被重写
  • 修饰变量
    • 必须显性初始化
    • 值不可变
      1. 基本数据类型
      2. 引用数据类型,地址值不能变,但是内部变量可以修改

super关键字

  1. super不是引用数据类型,也不代表内存地址
  2. super代表的是 所有父类的特征,并不是父类对象,super并没有在堆中创建对象。
// A code block
class  person extends father(){
	 System.out.println(super.name);//有结果
	  System.out.println(super.super.name);//错误
}
  1. super关键字依然无法在static修饰的方法中使用
  2. 可以调用父类中的构造方法,但是必须放置在第一语句
super的使用范围
  • 绝大部分情况,是在继承关系中使用
  • 小部分情况下,是在泛型中使用
      java规定,在执行构造函数之前必须执行父类的构造函数,直到这个类是java.lang.Object类的构造函数
super和this为什么不能同时出现:

因为在构造的时候只需要调用父类的super()作为初始化父类一次,如果super(…)和this(…)同时出现的话,那么就会出现初始化父类两次的不安全操作,因为当super(…)和this(…)同时出现的时候,在调用完了super(…)之后还会执行this(…),而this(…)中又会自动调用super(),这就造成了调用两次super()的结果(这是假设super(…)和this(…)同时出现时候,super(…)在this(…)前面,反过来出现也一样

重要:子类创建对象,必须先执行父类中的构造方法

如果子类想使用父类中的属性(在不同的包),那么我们需要使用`protected`
对属性进行修饰,但是这句话破坏了面向对象编程的封装性,我们实际开发 
中绝大部分的属性都应该设置为`private`,很少使用继承的复用性。

多态

多态:个人理解-是针对于引用数据类型,因为=两端的数据类型不一致,才产生了多态(本质就是数据类型转换),需要存在继承关系

  • 父类声明 变量 = 引用子类对象的地址 ,对象上转型
  • 不能将父类对象直接转换为子类
  • 对象下转型的使用前提对象上转型 : 子类声明 变量 = (强制转换符子类类型)父类引用子类对象
  • 可以通过instantceof来判断类与类之间是否存在继承关系

注意

public class Demo03{
    public static void main(String...arguments){
        Person p1 = new Student();//对象上转型
        //p1.study();//错误原因如下:
        //在编译阶段(javac.exe),p1所属的类型为Person类型,只能使用Person中定义的方法或者属性
        
        //javac.exe 检查p1是Person类型
        //但是,如果当我们处于运行阶段(java.exe),p1“真正”所有的类型为Student
        System.out.println("检查变量真正的类型:"+p1.getClass().getName());
        System.out.println(p1.name);
        p1.say();//调用的是继承过来的方法
       //重写方法
        p1.eat();//第一步:检查编译阶段Person类中是否存在该方法,第二步:运行阶段Student对象
    }
}

class Person{
    
    String name = "悟空";
    
    public void say(){
        System.out.println("人说话");
    }
    
    public void eat(){
        System.out.println("人吃饭");
    }
    
}
class Student extends Person{ 
    String name = "八戒";
    
    public void study(){
        System.out.println("学生学习");
    }
    public void eat(){
        System.out.println("学生吃便当");
    }
    
}

这里对象上转型找不到子类中的方法原因是:
在编译阶段(javac.exe),p1所属的类型为Person 类型,只能使用Person 中定义的方法或者属性
注:java中变量按照变量的阶段划分:可分为编译阶段变量运行阶段变量
上转型对象在编译期间检查父类有无该方法,在运行期间使用子类方法

使用对象上转型:

1.在编译阶段,无法使用子类中扩展的新方法student()

2.在编译阶段,使用的属性和方法在父类中必须存在

3.在运行阶段,

​ A.属性依然还是使用父类中值

​ B.检查调用的方法,在子类中是否重写,如果重写执行的是重写后的方法,如果没有重写,执行的是继承的方法

使用对象下转型:

一定要判断所有类的类型,容易造成类型转换异常

public class Demo04{
    public static void main(String...arguments){
        
        Animal animal = new Dog();
        //需要运算符 instanceof 判断类的类型
        System.out.println(animal instanceof Cat);
        System.out.println(animal instanceof Animal);
        System.out.println(animal instanceof Object);
        if(animal instanceof Cat){
            Cat cat = (Cat)animal;
            cat.run();
        }
        if(animal instanceof Dog){
            Dog dog = (Dog)animal;
            dog.run();
        }
        //类型转换异常
        //Dog dog = (Dog)animal;
        //java.lang.ClassCastException: Cat cannot be cast to Dog
        //dog.run();
        //cat类和dog并无继承关系
       
    }
}

class Animal{
    public void run(){
        System.out.println("动物跑");
    }
}
class Cat extends Animal{
    public void run(){
        System.out.println("猫。。。跳");
    }
}
class Dog extends Animal{
     public void run(){
        System.out.println("狗。。。跑");
    }
    
}

隐藏代码

注释内容为隐藏代码

public class Demo03 /*extends Object*/{
    /*
    public Demo03{
        super();
    }
    */
    public static void main(String...args){

       // return;
    }
}
public interface Demo01{
 /*public abstract*/ int m2();//个人推荐
    public /*abstract*/ int m3();
}

getClass():方法返回的是运行阶段的堆内存中实际使用的类

方法

equals()方法

对于字符串变量来说:

  • "=="比较两个变量本身的值,即两个对象在内存中的首地址。

  • “equals()”比较字符串中所包含的内容是否相同。
      
      对于非字符串变量来说,"=="和"equals"方法的作用是相同的,都是用来比较其对象在堆内存的首地址,即用来比较两个引用变量是否指向同一个对象。

  • 如果是基本类型比较,那么只能用==来比较,不能用equals

  • 对于基本类型的包装类型,比如Boolean、Character、Byte、Shot、Integer、Long、Float、Double等的引用变量,==是比较地址的,而equals是比较内容的
    若是想比较内容,则要重写equals方法:

 public boolean equals(Object obj) {
        System.out.println("重写后的equals方法");
        if(this==obj){
            return true; 
        }
        Student student = (Student)obj;//对象下转型,必须先有对象上转型
        boolean flag = false;
        if(this.name.equals(student.name)){
            flag = true;
        }else{
            flag = false;
        }
        if(this.age==student.age){
            flag = true;
        }else{
            flag = false;
        }
        return flag;
    }
hashCode()方法

Java对象的eqauls方法和hashCode方法是这样规定的:

1、相等(相同)的对象必须具有相等的哈希码(或者散列码)。

2、如果两个对象的hashCode相同,它们并不一定相同。

在集合查找时,hashcode能大大降低对象比较次数,提高查找效率!
  当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

注意:

  • 重写equals方法,必须重写hashcode方法,提高比较的效率
  • 重写hashcode方法,可以不用重写equals方法

static与单例设计模式

单例模式:不允许使用new来创建对象,就是任何方法调用此类生成的实例只有一个。需要将默认的构造方法设置成private。然后使用一个静态方法生产实例。

单例模式的优点:

1,它能够避免对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间。

2,能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。
main()主方法:

public class Demo02 {
    public static void main(String...args){
       //错误: Student() 在 Student 中是 private 访问控制
       //Student s1 = new Student();
        Student s2 = Student.getInstance();
        System.out.println(s2);
        s2.setName("悟空");
        s2.setAge(100);
        System.out.println(s2);
        
        Student s3 = Student.getInstance();
        System.out.println(s3);
        s3.setName("八戒");
        s3.setAge(200);
        //输出结果:特别重要
        System.out.println(s2);
        System.out.println(s3);
    }
}
单例设计模式-饿汉设计方式
class Student{
    //私有的构造方法:外部无法对该类进行实例化操作
    private Student(){}
    
    private String name;
    private int age;
    private static Student student = new Student();//单例设计模式-饿汉设计方式
    
    public static Student getInstance(){
        return student;
    }
    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public String toString() {
        return "name="+name+",age="+age;
    }

}

该类的构造函数是私有的,所以保证其他类不能实例化这个类,然后提供了一个静态实例并返回给调用者。饿汉模式是最简单的一种设计单例模式,在类加载的时候就创建实例,实例在整个程序周期都会存在。
饿汉模式的优缺点

优点:在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。

缺点:即使该单例没有用到也会被创建,而且在类加载之后就被创建,内存被浪费。
饿汉模式适合单例占用内存比较小,在初始化时就会被用到的情况。

单例设计模式-懒汉设计方式
class Student{
    //私有的构造方法:外部无法对该类进行实例化操作
    private Student(){}
    
    private String name;
    private int age;
    public static Student getInstance(){
        if(student==null){//单例设计模式-懒汉设计方式
            student = new Student();
        }
        return student;
    }
     
    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public String toString() {
        return "name="+name+",age="+age;
    }

}

懒汉模式就是在需要的时候进行实例化,如果单例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象。
缺点:安全性无法保证
懒汉模式适合某个单例使用的次数少,并且创建单例消耗的资源较多。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月色夜雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值