• 学习《Java核心技术》——第5章:继承


    学习《Java核心技术》——第5章:继承

    Java核心技术.卷I(第11版)

    第4章:对象与类

    1.继承

    使用extends关键字

    public class SubClazz extends ParentClazz{
        ...
    }
    
    • 1
    • 2
    • 3

    覆盖(override)或重写(overwrite)

    继承时期发生

    子类不能直接访问父类的私有字段,因此要使用getset方法

    调用父类的非私有方法,super.xxx()

    子类构造器

    public SubClazz(String s, int i){
        super(s,i);//调用父类同参数的构造器,对父类私有字段初始化
        c = "haha";
    }
    
    • 1
    • 2
    • 3
    • 4

    没有显式调用,则自动调用父类的无参构造器,否则报编译错误。

    归纳:要么默认调用无参构造器,要么就显式调用构造器

    多态(polymorphism):一个对象变量可以指示多种实际类型的现象

    比如,循环遍历一个父类对象数组,运行时虚拟机知道它的实际对象,并调用相对应的方法。

    动态绑定(dynamic binding):运行时自动选择对应合适的方法(不同重写过的方法或者说不同子类的相同方法)

    好处就是,对代码扩展来讲,非常好,虚拟机会自己确定具体的方法,并且不用修改其他部分代码。

    阻止继承(final类和方法):

    final关键字修饰的类和方法

    • 类不可以被继承
    • 子类不能覆盖(重写)这个final方法

    对于final字段,构造对象后,值不能被改变。

    final类的方法自动成为final,但是字段不会。

    强制类型转换:使用()

    double x = 3.14;
    int nx = (int)x;
    
    • 1
    • 2

    子类可以转成父类,没问题;父类转子类之前应该先instanceof判断一下

    a instanceof Clazz:判断a是否是Clazz类的实例

    对于a是null的情况,一定返回false,因为null不引用任何对象

    受保护访问,private

    父类的方法只被子类访问

    子类的方法访问父类的某个字段

    受保护字段只能被同一个包的类访问,一般这个不归规范,不利于OOP,不用

    受保护方法,被子类使用,不被其他类使用。(待测试加深印象,如Object的clone方法)

    2.this和super总结

    this

    • 调用该类的其他构造器,按照参数类型匹配
    • 指示隐式参数的引用,即调用此类的方法和字段

    super

    • 调用父类的方法

    • 调用父类的构造器

      此时,必须是构造器的第一句

    3.方法调用过程

    • 编译器查看对象的声明类型和方法名

      编译器会找出该类和其父类的所有可能被调用的方法

    • 编译器确定方法调用中的参数类型,找到对应匹配的方法

      这就是**重载解析**

    • 如果是private, static, final方法或者构造器,那么编译器就可以准确知道应该调用哪个方法。

      这就是静态绑定,就是只要在该类寻找就行,也不用涉及重载的问题。对应的是动态绑定

    • 程序运行并采用动态绑定调用方法时

      虚拟机为每个类准备了方法表(method table),调用方法时,虚拟机查表就好了。

    4.fianl总结

    final关键字修饰的类和方法

    • 类不可以被继承
    • 子类不能覆盖(重写)这个final方法

    对于final字段,构造对象后,值不能被改变。

    final类的方法自动成为final,但是字段不会。

    final的用意:确保被final修饰的方法和类不会在子类中改变语义,就是被覆盖重写,保证状态唯一。

    5.抽象类总结

    越顶层的类往往越具有抽象性,使用abstract声明该类;

    子类使用extends继承抽象类;

    即使类不含抽象方法,类也可以是抽象类;

    抽象类不可以被实例化;

    但可以定义一个抽象类的对象变量,用这个变量去引用非抽象子类的对象,此时去调用方法就会定位到具体实现的对应方法

    public abstract class Person{
        private String name;
        
        public Person(String name){
            this.name = name;
        }
        public abstract String getdescription();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    抽象方法类似占位符,具体使用时再具体实现。抽象方法在具体子类中都要被实现。

    扩展抽象类

    1. 子类也是抽象类。只实现了一部分的抽象方法
    2. 子类不是抽象类,这样就要定义抽象父类的全部方法

    6.访问控制修饰符总结

    • public

      公开,对外部可见

    • protected

      对本包和所有子类可见,一般就用在方法上,字段上不提倡

    • private

      仅对本类可见

    • default

      对本包可见

    那么就是三个层级:本类子类

    7. Object基类

    Object类型的变量可以引用任何类型的对象

    在Java中,只有基本类型(primitive type)不是对象,比如数值、字符和布尔类型的值都不是对象

    数组类型是对象,扩展了Object类

    equals方法:(不详细,待补充)

    检测两个对象是否相等,Object类实现的equal方法是比较两个对象的引用。

    Object.equals(a,b)//a,b都为null时,返回ture
    
    • 1

    equals方法要具有以下特性:

    • 自反性:对于任何非空引用x,x.equals(x)应该返回ture
    • 对称性:对于任何引用x和y,x.equals(y)和y.euqals(x)返回一致
    • 传递性:对于任何引用x,y,z,如果x=y,y=z,则x=z
    • 一致性:如果x和y引用的对象没有发生变化,则x.equals(y)的结果不变
    • 对任意非空引用x,x.equals(null)返回false

    hashCode方法

    hash code(散列码):传入对象,返回整型值

    每个对象都继承Object类,因此,每个对象默认有一个散列码,而且一般不相同

    两个相等的对象的散列码也要一致。因此,如果重新定义了equals方法,hashCode方法可能也要重新定义。

    null的散列码是0

    toString方法

    返回表示对象值的一个字符串

    对象与一个字符串通过操作符“+”连接,Java编译器自动调用toString方法获得这个对象的字符串描述。即,

    /**
     * 自动调用toString方法
     */
    x.toString();
    // ==
    "" + x;
    
    System.out.println(x);
    // ==
    system。呕吐。println(x.tostring());
    
    //字符串输出
    String s = Arrary.tostring(arrNum);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Class类:

    Class是一个类,

    Class getClass()方法,返回包含对象信息的类对象

    String getName(), 返回类名

    Class getSuperclass(), 以Class对象形式返回这个类的父类

    8.泛型数组

    Java允许运行时确定数组大小

    ArrayList,数组列表,类似数组,可以自动调整数组容量。但是不能通过索引访问对象元素

    ArrayList,是一个有类型参数(type parameter)的泛型类(generic class)

    ArrayList<Person> p = new ArrayList<>(); //泛型为Person
    p.add(new person());
    
    • 1
    • 2

    add()方法的内部机制:如果调用add而内部数组已经满了,那么数组列表会创建一个更大的数组,并把所有的对象都拷贝过去。

    ensureCapacity(100),表示可以add100次,超过后就创建一个更大的数组列表

    add和set方法接受任意类型的对象,所以需要注意,在强制转换的时候就可能出现问题。

    数组列表的元素拷贝到数组中:

    var list = new ArrayList<X>();
    //todo: add element
    var a = new X[list.size()];
    list.toArray(a);//拷贝成数组,便于索引访问
    
    • 1
    • 2
    • 3
    • 4

    ArrayList插入(add)和删除(remove),插入和删除,都要整体移动后面的所有元素,使用链表插入和删除的效率高。

    9.对象包装器和自动装箱

    所有的基本类型都有一个与之对应的类,便于将基本类型转换成对象。

    这些类就是包装器(wrapper),这些类的父类是Number类。包装器类都是final类,因此不能派生出子类。

    在泛型中,<>只能使用包装器类,不能使用基本类型。

    var list = new ArrayList<Integer>();
    
    • 1

    自动装箱(autoboxing):基本类型的值封装成对应对象

    list.add(3);
    //自动变换成
    list.add(Integer.valueOf(3));
    
    • 1
    • 2
    • 3

    与之对应的,自动拆箱:对象自动转换成基本类型的值

    // list是一个整型数组列表
    int n = list.get(i);
    //自动转换成
    int n = list.get(i).intValue()
    
    • 1
    • 2
    • 3
    • 4

    装箱和拆箱都是编译器干的活,虚拟机只是负责执行这些字节码。

    类型转换

    String s = "3";
    int x = Integer.paraseInt(s);//静态方法paraseInt()
    
    • 1
    • 2

    10.参数数量可变的方法

    type... var

    public static double max(double... values){
        double largest = Double.NEGATIVE_INFINITY;
        for (double v:values) if (v > largest) largest = v;
        return largest;
    }
    double m = max(3.1, 40.1, -5);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    分析:编译器将new double[]{3.1, 40.1, -5}传给max方法。

    11.枚举类 enum关键字

    声明的是一个类,枚举类。

    经典模式:

    public enum Size{SMALL, MEDIUM, LARGE, EXTRA_LARGE}
    
    • 1

    比较全的:

    public enum Size{
        SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");//枚举常量
        private String abbreviation;
        
        private Size(String abbreviation){ this.abbreviation = abbreviation;}
        public String getAbbreviation(){return abbreviation;}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    enum构造器一定是私有的,否则语法错误。

    所有的枚举类都是Enum类。

    Size.SMALL.toString(); //"SMALL"
    Size s = Enum.valueOf(Size.class, "SMALL");//s=Size.SMALL
    Size[] values = Size.values();//返回全部枚举值的数组
    int ordinal();//返回枚举常量的索引(下标)
    
    • 1
    • 2
    • 3
    • 4

    12.反射

    能够分析类能力的程序成为反射(reflective),用来编写动态操纵Java代码的程序。

    • 在运行时分析类的能力
    • 在运行时检查对象
    • 实现泛型数组操纵对象
    • 利用Method对象

    比如:支持 用户界面生成器;对象关系映射器;其他需要动态查询类能力的开发工具。

    Class类

    程序运行期间,Java运行时系统会对每个对象维护一个运行时类型标识,跟踪每个对象所属的类,以便执行正确的方法。

    虚拟机为每个类型(包括基础类型)管理一个唯一的Class对象,因此可以使用“===”作比较。

    与instanceof不同的是,他不涵括子类,只与自身类比较。

    static Class forName(String className): 返回一个Class对象,表示名为className的类。

    利用反射分析类的能力:

    最重要:检查类的结构

    java.lang.reflect包中Field、Method和Constructor三个类分别用来描述类的字段、方法和构造器;使用这三个类的getName方法来返回。具体看API。

    使用反射在运行时分析对象

    也就是动态获取某个实例的某个字段的具体值,诸如此类。

    步骤:

    • 获取该对象的类,返回Class类的对象
    • 获取字段
    • 通过字段,传入具体对象,获取该对象的某个字段的值。也可以设置值。

    使用反射编写泛型数组

    。。。跳过

    调用任意方法和构造器

    。。。跳过

    13.声明异常入门

    • 非检查型异常

      比如越界错误和访问null引用,就是运行时候报错

    • 检查型异常

      编译器会检查,就是编译报错

    在方法上使用throws XXXException,调用该方法的任何方法也要一样声明,包括main方法。

    这样会捕获异常而不会因为异常终止程序。

    14.继承的设计技巧

    • 公共操作和字段放在父类中

    • 不使用受保护的字段

      protected,对本包和所有子类可见,因此子类或同一包的其他类可以直接访问父类的字段,破坏了封装性

    • 使用继承实现“is-a”关系

    • 除非所有继承的方法都有意义,否则就不要使用继承

    • 在覆盖方法时,不要改变预期的行为

    • 使用多态,而不要使用类型信息

      也就是如果多个具体行为都具有同一个概念,就是用多态来完成,不要死板的使用类型类判断在做具体的行为。

      可以将这个放在父类或者接口中,再具体实现。

    • 不要滥用反射

  • 相关阅读:
    Flutter:定位装饰权重组件
    CPK分析工具页面设计源码
    审计和风控做什么——企业审计和风控工作的相同和不同
    电力系统直流潮流分析【N-1】(Matlab代码实现)
    仪表板支持水印设置,数据集新增脱敏规则支持,DataEase开源数据可视化分析平台v1.17.0发布
    Android手机防沉迷软件的基本原理
    Laxcus为什么叫分布式操作系统,和其它操作系统有什么不同?
    Java 包 import package final
    洛谷千题详解 | P1016 [NOIP1999 提高组] 旅行家的预算【C++、Java、Python语言】
    IntelliJ_IDEA的下载和安装的准备
  • 原文地址:https://blog.csdn.net/qq_37774098/article/details/126653178