Java基础之继承

简介: 引用隐式参数;调用该类其他的构造器。

继承


this关键字的用途

引用隐式参数;

调用该类其他的构造器。

super关键字的用途

调用超类的方法。

调用超类的构造器。

多态

可以通过 is-a规则,它表明子类的每个对象也是超类的对象。例如,每个

经理都是雇员。因此,将Manager类设计为Employee类的子类是显而易见的,反之不然,并不是每一名雇员都是经理。


is-a 规则的另一种表述法是置换法则。它表明程序中出现超类对象的任何地方都可以用子类对象置换。

例如:可以将一个子类的对象赋给超类变量

Employee e;
e=new Employee();
e=new Manager();


理解方法调用

弄清楚如何在对象上应用方法调用非常重要。下面假设要调用 x.f(args),隐式参数x声明为类C的一个对象。下面是调用过程的详细描述:

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


假设调用 x.f(args),且隐式参数x声明为C类的对象。需要注意的是:有可能存在多个名字为f,但参数类型不一样的方法。例如,可能存在

方法 f(int)和方法f(String)。编译器将会一一列举所有C类中名为f的方法和其超类中访问属性为public且名为f的方法。(超类的私有方法不可访问)

2. 编译器将查看调用方法时提供的参数类型。

如果在所有名为f的方法中存在一个与提供的参数类型完全匹配,就选择这个方法。这个过程称为**重载解析**。例如,对于调用 `x.f("HelloWorld")`
来说,编译器将会挑选 `f(String)`,而不是 `f(int)` 。由于允许类型转换(int 可以转换成double,Manager可以转换成Employee,等等),所以这个过程
可能很复杂。如果编译器没有找到与参数类型匹配的方法,或者发现经过类型转换后有多个方法与之匹配,就会报告一个错误。
至此,编译器已获得需要调用的方法名字和参数类型。
**方法的名字和参数列表称为方法的签名,返回类型不是签名的一部分。**


3. 如果是Private方法、static方法、final方法或者构造器,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方法称为静态绑定。与此对应的是,

调用的方法依赖于隐式参数的实际类型,并且在运行时实现动态绑定。在我们列举的示例中,编译器采用动态绑定的方式生成一条调用 f(String) 的指令。


当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与x所引用对象的实际类型最合适的那个类的方法。


假设x的实际类型是D,它是C类的子类。如果D类定义了方法 f(String),就直接调用它;否则,将在D类的超类中寻找 f(String),


每次调用方法都要进行搜索,时间开销相当大。因此,虚拟机预先为每个类创建了一个方法表(method table),其中列出了所有方法的签名和实际调用的方法。这样一来。

在真正调用方法的时候,虚拟机仅查找这个表就行了。在前面的例子中。虚拟机搜索D类的方法表,以便寻找与调用 f(String) 相匹配的方法。这个方法既有可能是 D.f(String),

也有可能是 X.f(String),这里的X是D的超类。这里需要提醒一点,如果调用 super.f(param),编译器将对隐式超类的方法表进行搜索。


现在,查看一下程序清单5-1中调用e.getSalary() 的详细过程。


动态绑定有一个非常重要的特性:无需对现存的代码进行修改,就可以对程序进行扩展。假设增加一个新类 Executive,并且变量e有可能引用这个类的对象,我们不需要

对包含调用 e.getSalary()的代码进行重新编译。如果e恰好引用一个Executive类的对象,就会自动地调用 Executive.getSalary()方法。


阻止继承:final类和方法

不允许扩展的类称为final类。


public final class Executive extends Manager{
    ...
 }

equal方法

Object类中的equals方法用于检测一个对象是否等于另外一个对象。**在Object类中,这个方法将判断两个对象是否具有相同的引用。如果

两个对象具有相同的引用,它们一定是相等的**。从这点上看,将其作为默认操作也是合乎情理的。


Java 语言规范要求equals方法具有下面的特性:

1. 自反性:对于任何非空引用x,x.equals(x) 应该返回true;

2. 对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)

也应该返回true。

3. 传递性:对于任何引用x,y和z,如果x.equals(y) 返回true,y.equals(z) 返回true,

x.equals(z) 也应该返回true。

4. 一致性: 如果x和y引用的对象没有发生变化,反复调用x.equals(y) 应该返回同样的结果。

5. 对于任意非空引用x,x.equals(null) 应该返回false。


编写一个完美的equals方法的建议:


显式参数命名为 otherObject,稍后需要将它转换成另一个叫做other的变量。

检测this与otherObject是否引用同一个对象:


if(this==otherObject) return true;


检测otherObject是否为null,如果为null,返回false。


if(otherObject==null) return false


比较this与otherObject是否属于同一个类。如果euqals的语义在每个子类中有所改变,就使用

getClass 检测:


if(getClass()!=otherObject.getClass()) return false


将otherObject 转换为相应的类类型变量:

ClassName other=(ClassName)otherObject

例如: TODO 添加例子:


hashCode 方法

散列码是由对象导出的一个整型值。散列码是没有规律的。

字符串(String)的散列码是由内容导出的。

字符串缓存(StringBuffer)类中没有定义hashCode方法,它的散列码是由Object

类的默认hashCode方法导出的对象存储地址。


toString方法

设计子类的model,POJO时必须重写toString方法。并将子类域的描述添加进去。

toString 方法是一种非常有用的调试工具。在标准类库中,许多类都定义了toString方法。

以便用户能够获得一些有关对象状态的必要信息。


对象包装器与自动装箱

所有的基本类型都有一个与之对应的类。这些类称为包装器。这些对象包装器类拥有很明显的名字:

Integer,Long,Float,Double,Short,Byte,Character,Void和Boolean(前6个类派生于公共的超类Number)。对象

包装器类是不可变的,即一旦构造了包装器,就不允许更改包装器在其中的值。同时,对象包装器类还是final,因此不能定义它们的子类。


自动装箱

例如:在集合ArrayList中,下面的调用 list.add(3) 将自动地变换成 list.add(Integer.valueOf(3))

这种变换被称为自动装箱。

自动拆箱

当将一个Integer对象赋给一个int值时,将会自动地拆箱。也就是说,编译器将下列语句:

int n=list.get(i);

翻译成

int n=list.get(i).intValue()


大多数情况下,容易有一种假象,即基本类型与它们的对象包装器是一样的。只是它们的相等性不同。大家知道,

== 运算符也可以应用于对象包装器对象。只不过检测的是对象是否指向同一个存储区域,因此,下面的比较通常不会成立:

Integer a=1000;
 Integer b=1000;
 if(a==b) ...


然而,Java实现却有可能让它成立。如果经常出现的值包装到同一个对象中,这种比较就有可能成立。这种不确定的结果的结果

并不是我们所希望的。解决这个问题的办法是在两个包装器对象比较时调用equals 方法。


自动装箱规范要求boolean,byte,char<=127,介于-127~127之间的short和int被包装到固定的对象中。例如,如果前面的例子中将a和b

初始化为100,对他们进行比较的结果一定成立。


如果在一个条件表达式中混合使用Integer和Double类型,Integer值就会被拆箱,提升为double,再装箱为Double:

Integer n=1;
Double x=2.0;
System.out.println(true?n:x);  //Prints 1.0

装箱和拆箱是编译器认可的。而不是虚拟机。编译器在生成类的字节码时,插入必要的方法调用。虚拟机只是执行这些字节码。

字符串转成整型。

int x=Integer.parseInt(s);

Java方法都是值传递的。如下所示:

public static void main(String[] args) {
        int x=3;
        triple(x);
        System.out.println("计算后的x="+x);
    }
    public static void triple(int x) {
        x = 3 * x;
        System.out.println("经过运算的x="+x);
    }


输出结果是:经过运算的x=9,计算后的x=3


例如:

public static void main(String[] args) {
          Employee outEmployee = new Employee();
          outEmployee.setName("测试");
          call(outEmployee);
          System.out.println("employee:"+outEmployee.getName());
      }
      public static void call(Employee inEmployee) {
          Employee employee = new Employee();
          employee.setName("cba");
          inEmployee.setName("abc");
          inEmployee = employee;
      }

输出结果是:employee:abc

分析:在此程序中,这次调用等于声明了两个变量outEmployee和inEmployee,他们指向的是同一个地址,

调用call方法。只是将outEmployee的地址传给了inEmployee,而outEmployee本身并没有传递过去(也就是引用没有传递过去)

所以在再次赋值时只是影响了inEmployee。

这样的传递方式只能称为址传递,或者是引用对象传递,而不能说是引用传递。


相关文章
|
3月前
|
Java 程序员
Java中的继承和多态:理解面向对象编程的核心概念
【8月更文挑战第22天】在Java的世界中,继承和多态不仅仅是编程技巧,它们是构建可维护、可扩展软件架构的基石。通过本文,我们将深入探讨这两个概念,并揭示它们如何共同作用于面向对象编程(OOP)的实践之中。你将了解继承如何简化代码重用,以及多态如何为程序提供灵活性和扩展性。让我们启程,探索Java语言中这些强大特性的秘密。
|
6天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
11 3
|
6天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
18 2
|
6天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
15 2
|
6天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
16 1
|
2月前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
17天前
|
Java 测试技术 编译器
Java零基础-继承详解!
【10月更文挑战第4天】Java零基础教学篇,手把手实践教学!
19 2
|
21天前
|
Java 编译器
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
19 5
|
21天前
|
Java
java继承和多态详解
java继承和多态详解
35 5
|
21天前
|
Java 编译器
【一步一步了解Java系列】:子类继承以及代码块的初始化
【一步一步了解Java系列】:子类继承以及代码块的初始化
20 3