关键字:this

this 是什么

在 Java 中,this 关键字不算难理解,它的作用和其词义很接近。

  • 它在方法(准确的说是实例方法或非 static 的方法)内部使用,表示调用该方法的对象
  • 它在构造器内部使用,表示该构造器正在初始化的对象。

this 可以调用的结构:成员变量、方法和构造器

什么时候使用 this

实例方法或构造器中使用当前对象的成员

在实例方法或构造器中,如果使用当前类的成员变量或成员方法可以在其前面添加 this,增强程序的可读性。不过,通常我们都习惯省略 this

但是,当形参与成员变量同名时,如果在方法内或构造器内需要使用成员变量,必须添加 this 来表明该变量是类的成员变量。即:我们可以用 this 来区分成员变量和局部变量。比如:

另外,使用 this 访问属性和方法时,如果在本类中未找到,会从父类中查找。这个在继承中会讲到。

同一个类中构造器互相调用

this 可以作为一个类中构造器相互调用的特殊格式

  • this():调用本类的无参构造器
  • this(实参列表):调用本类的有参构造器

注意:

  • 不能出现递归调用。比如,调用自身构造器。
    • 推论:如果一个类中声明了 n 个构造器,则最多有 n - 1 个构造器中使用了”this(形参列表)
  • this()this(实参列表) 只能声明在构造器首行。
    • 推论:在类的一个构造器中,最多只能声明一个”this(参数列表)

面向对象特征二:继承(Inheritance)

继承的概述

生活中的继承

财产继承:

绿化:前人栽树,后人乘凉

样貌,继承之外,是不是还可以”进化”:

继承有延续(下一代延续上一代的基因、财富)、扩展(下一代和上一代又有所不同)的意思。

Java 中的继承

角度一:从上而下

通过继承,简化 Student 类的定义:

说明:Student 类继承了父类 Person 的所有属性和方法,并增加了一个属性 schoolPerson 中的属性和方法,Student 都可以使用。

角度二:从下而上

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类中无需再定义这些属性和行为,只需要和抽取出来的类构成继承关系。如图所示:

继承的好处

  • 继承的出现减少了代码冗余,提高了代码的复用性。
  • 继承的出现,更有利于功能的扩展。
  • 继承的出现让类与类之间产生了 is-a 的关系,为多态的使用提供了前提。
    • 继承描述事物之间的所属关系,这种关系是:is-a 的关系。可见,父类更通用、更一般,子类更具体。

注意:不要仅为了获取其他类中某个功能而去继承!

继承的语法

通过 extends 关键字,可以声明一个类 B 继承另外一个类 A,定义格式如下:

[修饰符] class 类A {
	...
}
 
[修饰符] class 类B extends 类A {
	...
}
  • 类 B,称为子类、派生类(derived class)、SubClass
  • 类 A,称为父类、超类、基类(base class)、SuperClass

继承性的细节说明

子类会继承父类所有的实例变量和实例方法

从类的定义来看,类是一类具有相同特性的事物的抽象描述。父类是所有子类共同特征的抽象描述。而实例变量和实例方法就是事物的特征,那么父类中声明的实例变量和实例方法代表子类事物也有这个特征。

  • 当子类对象被创建时,在堆中给对象申请内存时,就要看子类和父类都声明了什么实例变量,这些实例变量都要分配内存。
  • 当子类对象调用方法时,编译器会先在子类模板中看该类是否有这个方法,如果没找到,会看它的父类甚至父类的父类是否声明了这个方法,遵循从下往上找的顺序,找到了就停止,一直到根父类都没有找到,就会报编译错误。

所以继承意味着子类的对象除了看子类的类模板还要看父类的类模板。

子类不能直接访问父类中私有的(private)的成员变量和方法

子类虽会继承父类私有(private)的成员变量,但子类不能对继承的私有成员变量直接进行访问,可通过继承的 getter/setter 方法进行访问。如图所示:

在 Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”

子类在继承父类以后,还可以定义自己特有的方法,这就可以看做是对父类功能上的扩展。

Java 支持多层继承(继承体系)

说明:

  • 一个父类可以同时拥有多个子类
  • 子类和父类是一种相对的概念
  • 顶层父类是 Object 类。所有的类默认继承 Object,作为父类。

Java 只支持单继承,不支持多重继承

public class A {}
class B extends A {}
 
// 一个类只能有一个父类,不可以有多个直接父类
class C extends B {} 	// ok
class C extends A, B {}	// error

方法的重写(override/overwrite)

父类的所有方法子类都会继承,但是当某个方法被继承到子类之后,子类觉得父类原来的实现不适合于自己当前的类,该怎么办呢?子类可以对从父类中继承来的方法进行改造,我们称为方法的重写(override、overwrite)。也称为方法的重置、覆盖。

在程序执行时,子类的方法将覆盖父类的方法。

@Override 使用说明:写在方法上面,用来检测是不是满足重写方法的要求。这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。建议保留,这样编译器可以帮助我们检查格式,另外也可以让阅读源代码的程序员清晰的知道这是一个重写的方法。

方法重写的要求

  1. 子类重写的方法必须和父类被重写的方法具有相同的函数签名(方法名称、参数列表)。
  2. 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型。
    • 注意:如果返回值类型是基本数据类型和 void,那么必须是相同
  3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限。
    • public > protected > 缺省 > private
    • 注意:①父类私有方法不能重写 ②跨包的父类缺省的方法也不能重写
  4. 子类方法抛出的异常不能大于父类被重写方法的异常
  5. 子类与父类中同名同参数的方法必须同时声明为非 static 的(即为重写),或者同时声明为 static 的(不是重写)。因为 static 方法是属于类的,子类无法覆盖父类的方法。

方法重载 VS. 方法重写

  • 方法的重载:同一类中不同函数复用同一个函数名
    • 方法名相同,形参列表不同,不看返回值类型。
  • 方法的重写:子类扩展/屏蔽父类方法

再谈封装性中的 4 种权限修饰

4 种权限修饰符

  • 外部类要跨包使用必须是 public,否则仅限于本包使用
    • 外部类的权限修饰符如果缺省,本包使用没问题
    • 外部类的权限修饰符如果缺省,跨包使用有问题
  • 成员的权限修饰符问题
    • 本包下使用:成员的权限修饰符可以是 publicprotected、缺省
    • 跨包下使用:要求严格
    • 跨包使用时,如果类的权限修饰符缺省,成员权限修饰符 > 类的权限修饰符也没有意义

关键字:super

super 的理解

在 Java 类中使用 super 来调用父类中的指定操作:

  • super 可用于访问父类中定义的属性
  • super 可用于调用父类中定义的成员方法
  • super 可用于在子类构造器中调用父类的构造器

注意:

  • 尤其当子父类出现同名成员时,可以用 super 表明调用的是父类中的成员
  • super 的追溯不仅限于直接父类
  • superthis 的用法相像,this 代表本类对象的引用,super 代表父类的内存空间的标识

super 的使用场景

子类中调用父类被重写的方法

  • 如果子类没有重写父类的方法,只要权限修饰符允许,在子类中完全可以直接调用父类的方法;
  • 如果子类重写了父类的方法,在子类中需要通过 super. 才能调用父类被重写的方法,否则默认调用的子类重写的方法

总结:

  • 方法前面没有 super.this.:先从子类找匹配方法,如果没有,再从直接父类找,再没有,继续往上追溯
  • 方法前面有 this.:先从子类找匹配方法,如果没有,再从直接父类找,再没有,继续往上追溯
  • 方法前面有 super.:从当前子类的直接父类找,如果没有,继续往上追溯

子类中调用父类中同名的成员变量

  • 如果实例变量与局部变量重名,可以在实例变量前面加 this. 进行区别
  • 如果子类实例变量和父类实例变量重名,并且父类的该实例变量在子类仍然可见,在子类中要访问父类声明的实例变量需要在父类实例变量前加 super.,否则默认访问的是子类自己声明的实例变量
  • 如果父子类实例变量没有重名,只要权限修饰符允许,在子类中完全可以直接访问父类中声明的实例变量,也可以用 this. 访问,也可以用 super. 访问

总结:起点不同(就近原则)

  • 变量前面没有 super.this.
    • 在构造器、代码块、方法中如果出现使用某个变量,先查看是否是当前块声明的局部变量
    • 如果不是局部变量,先从当前执行代码的本类去找成员变量
    • 如果从当前执行代码的本类中没有找到,会往上找父类声明的成员变量(权限修饰符允许在子类中访问的)
  • 变量前面有 this.
    • 通过 this 找成员变量时,先从当前执行代码的本类去找成员变量
    • 如果从当前执行代码的本类中没有找到,会往上找父类声明的成员变量(权限修饰符允许在子类中访问的)
  • 变量前面 super.
    • 通过 super 找成员变量,直接从当前执行代码的直接父类去找成员变量(权限修饰符允许在子类中访问的)
    • 如果直接父类没有,就去父类的父类中找(权限修饰符允许在子类中访问的)

特别说明:应该避免子类声明和父类重名的成员变量

在阿里的开发规范等文档中都做出明确说明:

子类构造器中调用父类构造器

  1. 子类继承父类时,不会继承父类的构造器。只能通过“super(形参列表)”的方式调用父类指定的构造器。
  2. 规定:“super(形参列表)”,必须声明在构造器的首行。
  3. 我们前面讲过,在构造器的首行可以使用”this(形参列表)”,调用本类中重载的构造器。结合 2,结论:在构造器的首行,“this(形参列表)”和”super(形参列表)”只能二选一。
  4. 如果在子类构造器的首行既没有显示调用”this(形参列表)”,也没有显式调用”super(形参列表)”, ​ 则子类此构造器默认调用”super()”,即调用父类中空参的构造器。
  5. 由 3 和 4 得到结论:子类的任何一个构造器中,要么会调用本类中重载的构造器,要么会调用父类的构造器,只能是这两种情况之一。
  6. 由 5 得到:一个类中声明有 n 个构造器,最多有 n-1 个构造器中使用了”this(形参列表)”,则剩下的那个一定使用”super(形参列表)”。

开发中常见错误:如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有空参的构造器,则编译出错

小结:thissuper

thissuper 的意义:

  • this:当前对象
    • 在构造器和非静态代码块中,表示正在 new 的对象
    • 在实例方法中,表示调用当前方法的对象
  • super:引用父类声明的成员

thissuper 的使用格式:

  • this
    • this.成员变量:表示当前对象的某个成员变量,而不是局部变量
    • this.成员方法:表示当前对象的某个成员方法,完全可以省略 this.
    • this()this(实参列表):调用另一个构造器协助当前对象的实例化,只能在构造器首行,只会找本类的构造器,找不到就报错
  • super
    • super.成员变量:表示当前对象的某个成员变量,该成员变量在父类中声明的
    • super.成员方法:表示当前对象的某个成员方法,该成员方法在父类中声明的
    • super()super(实参列表):调用父类的构造器协助当前对象的实例化,只能在构造器首行,只会找直接父类的对应构造器,找不到就报错

子类对象实例化全过程

面向对象特征三:多态(Polymorphism)

一千个读者眼中有一千个哈姆雷特。

多态的形式和体现

对象的多态性

多态性,是面向对象中最重要的概念,在 Java 中的体现:对象的多态性:父类的引用指向子类的对象

格式:

父类类型 变量名 = 子类对象;
// 父类类型:指子类继承的父类类型,或者实现的接口类型

举例:

Person p = new Student();
Object o = new Person(); // Object 类型的变量 o,指向 Person 类型的对象
o = new Student(); // Object 类型的变量 o,指向 Student 类型的对象

对象的多态:在 Java 中,子类的对象可以替代父类的对象使用。所以,一个引用类型变量可能指向(引用)多种不同类型的对象

多态的理解

Java 引用变量有两个类型:编译时类型运行时类型

  • 编译时类型由声明该变量时使用的类型决定
  • 运行时类型由实际赋给该变量的对象决定

简称:编译时,看左边;运行时,看右边。

  • 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
  • 多态情况下:
    • “看左边”:看的是父类的引用(父类中不具备子类特有的方法)
    • “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)

多态的使用前提:

  1. 类的继承关系
  2. 方法的重写

多态常用在:

  • 方法内局部变量的赋值体现多态
  • 方法的形参声明体现多态
  • 方法返回值类型体现多态

为什么需要多态性

开发中,有时我们在设计一个数组、或一个成员变量、或一个方法的形参、返回值类型时,无法确定它具体的类型,只能确定它是某个系列的类型。

案例:

  1. 声明一个 Dog 类,包含 public void eat() 方法,输出“狗啃骨头”
  2. 声明一个 Cat 类,包含 public void eat() 方法,输出“猫吃鱼仔”
  3. 声明一个 Person 类,功能如下:
    • 包含一个属性:宠物
    • 包含领养宠物方法:public void adopt(宠物类型Pet)
    • 包含喂宠物吃东西的方法:public void feed(),实现为调用 宠物对象.eat() 方法

问题:

  • 从养狗切换到养猫怎么办?
    • 修改代码把 Dog 修改为 Cat
  • 或者有的人养狗,有的人养猫怎么办?
  • 要是还有更多其他宠物类型怎么办?

如果 Java 不支持多态,那么上面的问题将会非常麻烦,代码维护起来很难,扩展性很差。

多态的好处和弊端

好处:变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活、功能更强大,可维护性和扩展性更好了。

弊端:一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。

我认为这并不是弊端,而是多态的好处:屏蔽掉不关心的内容。真正要挑弊端的话,可能是动态绑定,牺牲了一部分性能

开发中:使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,提高了扩展性,符合开闭原则。

【开闭原则 OCP】

  • 对扩展开放,对修改关闭
  • 通俗解释:软件系统中的各种组件,如模块(Modules)、类(Classes)以及功能(Functions)等,应该在不修改现有代码的基础上,引入新功能

虚方法调用(Virtual Method Invocation)

在 Java 中虚方法是指在编译阶段不能确定方法的调用入口地址,在运行阶段才能确定的方法,即可能被重写的方法。

Person e = new Chinese();  
e.welcome(); // 调用 Chinese 类的 welcome() 方法

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

前提:Person 类中定义了 welcome() 方法,各个子类重写了 welcome()

执行:多态的情况下,调用对象的 welcome() 方法,实际执行的是子类重写的方法。

拓展:

  • 静态链接(早绑定):当一个字节码文件被装载进 JVM 内部时,如果被调用的目标方法在编译期可知,且运行期保持不变时。这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接。那么调用这样的方法,就称为非虚方法调用。比如调用静态方法、私有方法、final 方法、父类构造器、本类重载构造器等。
  • 动态链接(晚绑定):如果被调用的方法在编译期无法被确定下来,也就是说,只能够在程序运行期将调用方法的符号引用转换为直接引用,由于这种引用转换过程具备动态性,因此也就被称之为动态链接。调用这样的方法,就称为虚方法调用。比如调用重写的方法(针对父类)、实现的方法(针对接口)。

成员变量没有多态性

  • 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
  • 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量

向上转型与向下转型

首先,一个对象在 new 的时候创建是哪个类型的对象,它从头至尾都不会变。即:这个对象的运行时类型、本质的类型不会变。但是,把这个对象赋值给不同类型的变量时,这些变量的编译时类型却不同。

为什么要类型转换

因为多态,就一定会有把子类对象赋值给父类变量的时候,这个时候,在编译期间,就会出现类型转换的现象。

但是,使用父类变量接收了子类对象之后,我们就不能调用子类拥有,而父类没有的方法了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子类特有的方法,必须做类型转换,使得编译通过。

  • 向上转型:当左边的变量的类型(父类)> 右边对象/变量的类型(子类),我们就称为向上转型
    • 此时,编译时按照左边变量的类型处理,就只能调用父类中有的变量和方法,不能调用子类特有的变量和方法了
    • 但是,运行时,仍然是对象本身的类型,所以执行的方法是子类重写的方法体。
    • 此时,一定是安全的,而且也是自动完成的
  • 向下转型:当左边的变量的类型(子类)< 右边对象/变量的编译时类型(父类),我们就称为向下转型
    • 此时,编译时按照左边变量的类型处理,就可以调用子类特有的变量和方法了
    • 但是,运行时,仍然是对象本身的类型
    • 不是所有通过编译的向下转型都是正确的,可能会发生 ClassCastException,为了安全,可以通过 instanceof 关键字进行判断

如何向上或向下转型

  • 向上转型:自动完成(多态)
  • 向下转型:显式指定:(子类类型) 父类变量
package com.atguigu.polymorphism.grammar;
 
public class ClassCastTest {
    public static void main(String[] args) {
        // 没有类型转换
        Dog dog = new Dog(); // dog 的编译时类型和运行时类型都是 Dog
 
        // 向上转型(多态)
        Pet pet = new Dog(); // pet 的编译时类型是 Pet,运行时类型是 Dog
        pet.eat(); // 可以调用父类 Pet 有声明的方法 eat,但执行的是子类重写的 eat 方法体
		// pet.watchHouse(); // 不能调用子类特有,父类没有的方法 watchHouse
 
		// 向下转型
        Dog d = (Dog) pet;
        d.eat(); // 可以调用 eat 方法
        d.watchHouse(); // 可以调用子类扩展的方法 watchHouse
 
        Cat c = (Cat) pet;
        // 编译通过,因为从语法检查来说
        // pet 的编译时类型是 Pet,Cat 是 Pet 的子类,所以向下转型语法正确
        // 运行报错:ClassCastException
        // 因为 pet 变量的运行时类型是 Dog,Dog 和 Cat 之间是没有继承关系的
    }
}

instanceof 关键字

为了避免 ClassCastException 的发生,Java 提供了 instanceof 关键字,给引用变量做类型的校验。如下语法格式:

// 检验对象 a 是否是数据类型 A 的实例对象,返回值为 boolean 型
对象a instanceof 数据类型A

说明:

  • 只要用 instanceof 判断返回 true 的,那么强转为该类型就一定是安全的,不会报 ClassCastException 异常。
  • 如果对象 a 属于类 A 的子类 B,a instanceof A 值也为 true
  • 要求对象 a 所属的类与类 A 必须是子类和父类的关系,否则编译错误。

Object 类的使用

如何理解根父类

java.lang.Object 是类层次结构的根类,即所有其它类的父类。每个类都使用 Object 作为超类。

Object 类型的变量与除 Object 以外的任意引用数据类型的对象都存在多态引用

所有对象(包括数组),即:引用数据类型,都实现这个类的方法。

如果一个类没有特别指定父类,那么默认则继承自 Object 类。例如:

public class Person {
	// ...
}
 
// 等价于:
public class Person extends Object {
	// ...
}

Object 类的方法

根据 JDK 源代码及 Object 类的 API 文档,Object 类当中包含的方法有 11 个。这里我们主要关注其中的 6 个:

equals()

  1. ==:比较值是否相同
    • 基本类型比较值:只要两个变量的值相等,即为 true
    • 引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,才返回 true
      • 其实比较的也是值,只是引用类型的值是其引用对象的指针(内存地址)
    • 用“==”进行比较时,符号两边的数据类型必须兼容(相同类型或可自动转换),否则编译出错
  2. equals():所有类都继承了 Object,也就获得了 equals() 方法,还可以重写。
    • 只能比较引用类型,Object 类源码中 equals() 的作用与“==”相同:比较是否指向同一个对象

格式:

obj1.equals(obj2)

当用 equals() 方法进行比较时,FileStringDate、包装类(Wrapper Class)等,是比较类型及内容而不考虑引用的是否是同一个对象。原因:在这些类中重写了 Object 类的 equals() 方法。

重写 equals() 方法的原则:

  • 对称性:x.equals(y) 的返回值应该和 y.equals(x) 相同
  • 自反性:x.equals(x) 必须返回 true
  • 传递性:如果 x.equals(y)true,而且 y.equals(z)true,那么 z.equals(x) 也应该是 true
  • 幂等性:如果 x.equals(y) 返回 true,只要 xy 内容一直不变,不管重复调用 x.equals(y) 多少次,返回都是 true
  • x.equals(null) 永远返回 false(如果 xnull 会空指针异常)
  • x.equals(和 x 不同类型的对象) 永远返回 false

面试题:==equals 的区别

宋红康老师:从我面试的反馈,85% 的求职者“理直气壮”的回答错误

  • == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较是否引用的同一个对象(内存地址相同)
  • equals 的话,只能比较引用类型,它是属于 java.lang.Object 类里面的方法,如果该方法没有被重写,默认也是 == 的逻辑;我们可以看到 String 等类的 equals 方法是被重写过的,而且 String 类在日常开发中用的比较多,久而久之,形成了 equals 是比较值的错误观点。
  • 通常情况下,重写 equals 方法,会比较类中的相应属性是否都相等。

hashCode()

函数签名:public int hashCode()

作用:返回每个对象的 hash 值。(后续在集合框架章节重点讲解)

toString()

默认情况下,toString() 返回的是“对象的运行时类型@对象的 hashCode 值的十六进制形式”

在进行 String 与其它类型数据的连接操作时,自动调用 toString() 方法

Date now = new Date();
System.out.println("now=" + now);
// 相当于:
System.out.println("now=" + now.toString());

如果我们直接 System.out.println(对象),默认会自动调用这个对象的 toString()

因为 Java 的引用数据类型的变量中存储的实际是对象的内存地址,但是 Java 对程序员隐藏内存地址信息,所以不能直接将内存地址显示出来,所以当你打印对象时,JVM 帮你调用了对象的 toString()

可以根据需要在自定义类型中重写 toString() 方法,如 String 类重写了 toString() 方法,返回字符串的值。

clone()

clone() 可能会抛 CloneNotSupportedException

finalize()

  • 当对象被回收时,系统自动调用该对象的 finalize() 方法。(不是垃圾回收器调用的,是本类对象调用的)
    • 永远不要主动调用某个对象的 finalize 方法,应该交给垃圾回收机制
  • 什么时候被回收:当某个对象没有任何引用时,JVM 就认为这个对象是垃圾对象,就会在之后不确定的时间使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize() 方法。
  • 子类可以重写该方法,目的是在对象被清理之前执行必要的清理操作。比如,在方法内断开相关连接资源。
    • 如果重写该方法,让一个新的引用变量重新引用该对象,则会重新激活对象。
  • 在 JDK 9 中此方法已经被标记为过时的

getClass()

函数签名:public final Class<?> getClass()

作用:获取对象的运行时类型

因为 Java 有多态现象,所以一个引用数据类型的变量的编译时类型与运行时类型可能不一致,因此如果需要查看这个变量实际指向的对象的类型,需要用 getClass() 方法

public static void main(String[] args) {
	Object obj = new Person();
	System.out.println(obj.getClass()); // class com.atguigu.java.Person(运行时类型)
}

native 关键字的理解

使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++ 等非 Java 语言实现的,并且被编译成了 DLL,由 Java 去调用。

  • 在 Java 中定义一个 native 方法时,并不提供方法体。
  • 本地方法是有方法体的,用 C 语言编写。由于本地方法的方法体源码没有对我们开源,所以我们看不到方法体

为什么要用 native 方法?

Java 使用起来非常方便,然而有些层次的任务用 Java 实现起来不容易,或者我们对程序的效率很在意时,例如:需要与一些底层操作系统或某些硬件交换信息时的情况。native 方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解 Java 应用之外的繁琐的细节。

native 声明的方法,对于调用者,可以当做和其他 Java 方法一样使用

native 方法的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM 将控制调用本地方法的所有细节。