Java学习笔记:重点语法

这篇博客详细讲解了Java编程中的核心语法,包括方法定义、类与对象的创建、方法重载、构造方法、引用类型数组、继承、多态等概念。深入探讨了方法签名、构造方法的作用、引用类型数组的声明与初始化、继承的实现以及构造方法的重载。此外,还介绍了类的访问控制修饰符、final和static关键字的应用、抽象方法和抽象类、内部类以及接口的使用。通过这些内容,读者可以全面了解Java编程的基础知识。
摘要由CSDN通过智能技术生成

文章目录

一、方法定义:
①什么是方法?
  • 方法用于封装一段具有特定逻辑功能的代码。
  • 方法可以在程序中被反复调用。
  • 方法可以减少代码重复,便于程序的维护。
②定义方法:
  • 定义方法的五个要素:修饰词、返回值类型、方法名、参数列表、方法体

image-20210705105427550

  • 定义参数
    • 方法的参数是指:在调用时传递给方法,需要被方法处理的数据
    • 方法可有参数,也可以没有参数,有参数使得方法的处理更加灵活。在方法定义时,需要声明该方法所需要的参数变量。
    • 在方法调用时,会将实际的参数值传递给方法的参数变量;必须保证传递参数的类型和个数符合方法的声明
void setInformation(){}
void setInformation(String name){}
int sum(int data1,int data2){}
  • 方法调用结束后可以返回一个数据,称之为返回值。
  • 方法在声明时,必须指定返回值的类型:
    • 若方法不需要返回数据,将返回值类型声明为void。
    • 若方法需要返回数据,将返回值类型声明为特定数据类型。
③return语句:
  • return语句的作用在于结束方法且将数据返回给调用方。
return;

对于返回值为void的方法也可以使用return语句,此时该语句的作用仅仅在于结束方法调用。


二、类和对象:
①什么是类?
  • 类定义了一种抽象数据类型。

抽象数据类型:可以理解为将不同类型的数据的集合组成一个整体用来描述一种新的事务。

  • 类不但定义了抽象数据类型的组成(成员变量),同时还定义了可能对该类型实施的操作(方法)。
②定义一个类:
  • 定义类的成员变量

    • 类的定义包括“成员变量”的定义和“方法”的定义;其中“成员变量”用于描述该类型对象共同的数据结构
  class 类名{
      成员变量类型 变量名称;
      ……
  }
  • 对象创建后,其成员变量可以按照默认的方式进行初始化;
成员变量的类型默认初始值
数值类型(byte、short、int、long、char、float、double)0
boolean型false
引用类型null
  • 定义类的方法

    • 类的方法用于描述对象的行为,封装对象的功能
    class 类名{
    	修饰词 返回值类型 方法,名称([参数列表]){
        	方法体……
        }
        ……
    }
    
③使用new关键字创建对象:
  • 类定义完成后,可以使用new关键字创建对象。创建对象的过程通常称为实例化
new 类名();
  • 引用类型类型变量
    • 为了能够对实例化的对象进行访问控制,需要使用一个特殊的变量——引用

除了8种基本类型之外,用类、接口、数组等声明的变量都称之为引用类型变量,简称“引用”。

image-20210706204804171

  • 可以通过引用访问对象的成员变量或调用方法。
Student s = new Student();
s.name = "lisi"
s.age = 18;

三、方法重载:
①方法的签名:
  • 方法的签名包含如下两个方面:方法名参数列表
  • 在一个类中,不可以有两个方法的签名完全一样,即在一个类中不可以有两个方法的方法名和参数列表都是完全一样的。如果只是方法名一样,但是参数列表不一样是可以的
//错误举例:
public class Cashier{
	public boolean pay(double money){……}
    public boolean pay(double money){……}
}
//正确举例:
public class Cashier{
	public boolean pay(double money){……}
    public boolean pay(String cardId,String cardPwd){……}
}
②方法重载及其意义:
  • 在Java中,允许多个方法的名称一样,但是参数列表不同,称之为方法的重载(overload)。
public class Cashier{
	public boolean pay(double money){……}
    public boolean pay(String cardId,String cardPwd){……}
}
  • 编译器在编译时会根据签名来绑定调用不同的方法,我们可以把重载的方法看做是完全不同的方法,只不过是方法名相同而已

四、构造方法:
①构造方法语法结构:
  • 构造方法是在类中定义的方法,不同于其他的方法,构造方法的定义有如下两点规则:
    • 构造方法的名称必须和类名一样。
    • 构造方法没有返回值,但是也不能写void。
[访问修饰符] 类名(){
	//构造方法体
}
②通过构造方法初始化成员变量:
  • 构造方法常用于实现对象成员变量的初始化。
class Student{
    String name;
    int age;
    Student(String name1,int age1){
        name = name1;
        age = age1;
    }
}

class TestStudent{
    public static void main(String[] args{
        Student s = new Student("lisi",25);
    }
}

创建对象时,构造方法写在new关键字后,可以理解为——“new”创建了对象,而构造方法对该对象进行了初始化。

③this关键字的使用:
  • this关键字用在方法体中,用于指向调用该方法的当前对象;简单来说——“哪个对象调用方法,this指的就是哪个对象。”严格来讲在方法中需要通过this关键字指明当前对象。

    public void study(){
    	System.out.println(this.name+"在学习!");
    }
    
  • 为了方便起见,在没有歧义的情况下可以省略this。

    public void study(){
    	System.out.println(name+"在学习!");
    }
    
  • 在构造方法中,用来初始化成员变量的参数一般和成员变量取相同的名字,这样会有利于代码的可读性,但此时就必须通过this关键字来区分成员变量和参数了(this不可省略)。

    Student(String name,int age){
    	this.name = name;
        thia.age = age;
    }
    
④默认的构造方法:
  • 任何一个类都必须含有构造方法
  • 如果源程序中没有定义,编译器在编译时将为其添加一个无参的空构造方法(称之为“默认的构造方法”)。但是当定义了构造方法之后,Java编译器将不再添加默认的构造方法
class Student{
    String name;
    int age;
    Student(String name1,int age1){
        name = name1;
        age = age1;
    }
}

class TestStudent{
    public static void main(String[] args){
        Student s = new Student();
        //编译错误,当定义了构造方法之后,编译器就不会提供默认的构造方法了!
    }
}
⑤构造方法的重载:
  • 为了使用方便,可以对一个类定义多个构造方法,这些构造方法都有相同的名称(类名),方法参数不同;称之为构造方法的重载

  • 在创建对象时,Java编译器会根据不同参数调用不同的构造方法。

Student s1 = new Student("lisi",18);
//对应的构造方法为:
Student(String name1,int age1){
	……
}

Student s2 = new Student();
//对应的构造方法为:
Student(){
	……
}
  • 一个构造方法可以通过this关键字调用另一个重载的构造方法。
Student(String name1,int age1){
	name = name1;
	age = age1;
}

Student(){
    this("baby",1);
}

五、引用类型数组:
①数组也是一个对象:
  • 在Java中,数组属于引用数据类型
  • 数组对象在堆中存储,数组变量属于引用类型,存储数组对象的地址信息,指向数组对象

image-20210706220833817

数组的元素都可以看成数组对象的成员变量(只不过类型全部一样)。

②引用类型数组的声明及其初始化:
  • 引用类型数组的声明
    • 数组的元素可以是任何类型,当然也包括引用类型。

image-20210706222010232

  • 引用类型数组的初始化
    • 引用类型数组的默认初始值都是null
    • 如果希望每一个数组元素都指向具体的对象,需要针对每一个数组元素进行“new”运算。
Student[] array = new Student[3];

array[0] = new Student();
array[1] = new Student();
array[2] = new Student();
  • 数组元素可以为任意类型,也包括数组类型
int[][] array = new int[3][];

array[0] = new int[2];
array[1] = new int[3];
array[2] = new int[2];

array指向一个数组,该数组有三个元素,每一个元素都是int类型数组,长度分别是2,3,2。

  • 对于元素为数组的数组,如果每一个数组元素的长度一样,可以采用下面的方式进行声明。
int[][] array = new int[3][4];

这样的数组可以用来表示“矩阵”这样的数据结构;在其他语言中有专门表示这样结构的所谓二维数组,而严格来讲,Java中没有真正的二维数组。


六、继承:
①继承的过程:

image-20210707115117697

②extends关键字:
  • 通过extends关键字实现类的继承。
  • 派生类继承了超类的成员变量与成员方法;同时也可以定义自己的成员变量和成员方法
  • Java不支持多重继承,一个类只能继承一个超类,但是一个超类可以有多个派生类。
class Teacher extends Person{
	……
}

个人理解

  • Java不像其他的语言,可以继承多个超类;这样的好处就是“继承树”比较明了;也便于后面的相应操作(譬如对于一组类进行一个特性的更改,那么只要更改他们共同的超类就行了!);也就是说,所有类的超类都是Object类。

  • 另外,Java还有接口来辅助实现多种属性的继承,在一定意义上近乎实现了“多继承”。

③继承中的构造函数:
  • 派生类的构造方法中必须通过super关键字调用超类的构造方法。
  • 如果派生类的构造方法中没有调用超类的构造方法,Java编译器会自动加入对超类无参构造方法的调用(如果该超类没有无参的构造方法的话,会出现编译错误)。
public Student(){
    super();//编译器会自动加入
    ……
}

注意:super关键字必须位于派生类构造方法的第一行

个人理解:其实就如同建房子,不打地基,如何建房,所以先建立超类的对象,再建立派生类的对象!


七、向上造型:
  • 父类(超类)的引用可以指向派生类的对象
    • 一个派生类的对象可以向上造型为超类的类型。即,定义超类类型的引用可以指向派生类的对象。
class Person{
	String name;
    int age;
    Person(String name,int age){
        this.name = name;
        this.age = age;
    }
}

class Student extends Person{
    double grade;
    Student(String name,int age,double grade){
        super(name,age);
        this.grade = grade;
    }
    
    public void setGrade(double grade){
        this.grade = grade;
    }
    
    public static void main(String[] args){
        Person p = new Student("lisi",18,97);
        //超类的引用可以指向派生类的对象。
    }
}
  • 这种访问是受到限制的
    • 父类的引用可以指向子类的对象,但是通过父类的引用只能访问父类所定义的成员,不能访问子类扩展的部分
class Person{
	String name;
    int age;
    Person(String name,int age){
        this.name = name;
        this.age = age;
    }
}

class Student extends Person{
    double grade;
    Student(String name,int age,double grade){
        super(name,age);
        this.grade = grade;
    }
    
    public void setGrade(double grade){
        this.grade = grade;
    }
    
    public static void main(String[] args){
        Person p = new Student("lisi",18,97);
        
        p.setGrade(60);//error!
    }
}

注意:Java编译器会根据引用的类型,而不是对象的类型检查调用是否合理匹配


八、重写:
①方法的重写:
  • 子类可以重写(覆盖)继承自父类的方法,即方法名和参数列表与父类的方法相同;但方法的实现不同

  • 重写遵循的原则——“两同两小一大”原则:

    • 两同
      • 方法名称一样,参数列表一样。
    • 两小
      • 派生类方法的返回值类型小于或等于超类方法的返回值。
      • 派生类方法抛出的异常小于或等于超类方法的异常。
    • 一大
      • 派生类方法的访问权限大于或等于超类方法的访问权限。
public class Person {
    public void Print(){
        System.out.println("这是一个人!");
    }
}

public class Teacher extends Person{
    @Override
    public void Print() {
        System.out.println("这是一个老师!");
    }

    public static void main(String[] args) {
        Person p = new Teacher();
        p.Print();

        Teacher t = new Teacher();
        t.Print();
    }
}
//output:
// 这是一个老师!
//这是一个老师!

补充:当子类对象的重写方法被调用时(无论是通过子类的引用调用,还是通过父类的引用调用),运行的是子类的重写后的版本

②在重写中使用super关键字:
  • 子类在重写父类的方法时,可以通过super关键字调用父类的版本。
public class Person {
    public void Print(){
        System.out.println("这是一个人!");
    }
}

public class Teacher extends Person{
    @Override
    public void Print() {
        super.Print();
        System.out.println("这是一个老师!");
    }

    public static void main(String[] args) {
        Person p = new Teacher();
        p.Print();

        Teacher t = new Teacher();
        t.Print();
    }
}
//output:
//这是一个人!
//这是一个老师!
//这是一个人!
//这是一个老师!

补充:子类重写方法中的super.Print();调用了父类的版本,这样的语法通常用于子类的重写方法在父类方法的基础上进行功能扩展

③重写与重载的区别:
  • 区别一
    • 重载是指在一个类中定义多个方法名相同但是参数列表不同的方法,在编译时,根据参数的个数和类型来决定绑定哪个方法。
    • 重写是指在子类中定义和父类完全相同的方法,在程序运行时,根据对象的类型不同(而不是引用类型)而调用不同的版本
  • 区别二
    • 重载遵循所谓的“编译期绑定”,即在编译时根据参数变量的类型判断应该调用哪一个方法。
    • 重写遵循所谓的“运行期绑定”,即在运行时根据引用变量指向的实际对象类型调用方法。

九、package和import语句:
①package语句:
  • 定义类时需要指定类的名称,但如果仅仅将类名作为类的唯一标识,则不可避免的会出现命名冲突的问题,这会给组件复用和团队之间的合作带来很大的麻烦。
  • 在Java语言中,用包(package)的概念来解决命名冲突的问题。在定义一个类时,除了定义类的名称一般还要指定一个包名,定义包名的语法为:package 包名

个人理解:有点像C++里面的命名空间,将彼此之间间隔开来,形成不同的作用域。

package 包名;
class 类名{
    ……
}

补充:package语句必须写在Java源程序文件的最开始,在类定义之前。一旦使用package语句指定了包名,此时类的全称应该是“包名.类名”;这就使得在不同的包中定义相同的类而不会混淆。

  • 包名也可以有层次结构,在一个包中可以包含另外一个包(也就是说可以嵌套定义使用)。此时按照如下的方式写package语句。
package 包名1.包名2...包名n;
  • 如果各个公司或者开发组织的程序员都随心所欲的命名包名的话,仍然不能从根本上解决命名冲突的问题。因此,在指定包名的时候应该按照一定的规范,例如:
package org.apache.commons.lang.StringUtils

解释:StringUtils是类名,而org.apache.commons.lang是多层包名,其含义如下:org.apache表示公司或组织的信息(是这个公司或者组织域名的反写);commons表示项目的名称信息;lang表示模块的名称信息。

②import语句:
  • 访问一个类时需要使用该类的全称,但是这样的书写过于繁琐。
com.baidu.www.commons.lang.StringUtils stringutils = new com.baidu.www.commons.lang.StringUtils();
  • 可以通过import语句对类的全称进行声明:
import 类的全局限定名(包名+类名)
  • 通过import语句声明了类的全称后,该源文件中就可以直接使用类名来访问了。
import com.baidu.www.commons.lang.StringUtils;
class Test{
    public static void main(String[] args){
        StringUtils stringutils = new StringUtils();
    }
}

十、访问控制修饰符:
①访问控制修饰符:
  • private
    • private修饰的成员变量和方法仅仅只能在本类中调用
  • public
    • public修饰的成员变量和方法可以在任何地方被调用

补充:public修饰的内容是对外提供可以被调用的功能,需要相对稳定;private修饰的内容是对内实现的封装,如果“公开”会增加维护的成本。

  • protected
    • protected修饰的成员变量和方法可以被子类及同一个包中的类使用。
  • 默认访问控制
    • 默认访问控制即不书写任何访问控制符;默认访问控制的成员变量和方法可以被同一个包中的类调用。
②访问控制符修饰类:
  • 对于类的修饰可以使用public和默认方式;public修饰的类可以被任何一个类使用默认访问控制的类只可以被同一个包中的类使用
  • protected和private可以用于修饰内部类
③访问控制符修饰成员:
  • 访问控制符修饰成员(成员变量及成员方法)时的访问控制权限:
修饰符本类同一包中的类子类其他类
public可以可以可以可以
protected可以可以可以不能
默认可以可以不能不能
private可以不能不能不能

十一、final关键字:
①final修饰变量:
  • final关键字修饰变量,意为不可改变
  • final修饰成员变量,有两种初始化方式:

image-20210707221844831

  • final关键字也可以修饰局部变量使用之前初始化即可。
②final修饰方法:
  • final关键字修饰的方法不可以被重写
  • 使一个方法不能被重写的意义在于——防止子类在定义新方法时造成的“不经意”重写。

个人理解:所以后续功能可扩展性方法一定不能声明为final类型。

③final修饰类:
  • final关键字修饰的类不可以被继承
  • JDK中一些基础类库被定义为final,譬如:String、Math、Integer、Double……
  • 使一个类不能被继承的意义在于——可以保护类不被继承修改,可以控制滥用继承对系统造成的危害。

个人理解:你只能用它,不能进一步通过继承该类修改、添加其功能。


十二、static关键字:
①static修饰成员变量:
  • static修饰的成员变量不属于对象的数据结构;static变量是属于类的变量,通常可以通过类名来引用static引用。
  • static成员变量和类的信息一起存储在方法区,而不是在堆中,一个类的static成员变量只有“一份”,无论该类创建了多少对象。
class Student{
    private int studentId;
    private static int count;
    
    public Student(int studentId){
        this.studentId = studentId;
        System.out.println(++count);
    }
    
    public static void main(String[] args){
        Student s1 = new Student(2018);
        Student s2 = new Student(2020);
    }
}

image-20210708210617929

②static修饰方法:
  • 通常的方法都会涉及到对具体对象的操作,这些方法在调用时,需要隐式的传递对象的引用(this)
class Student{
    private int studentId;
    
    public setId(int studentId){
        this.studentId = studentId;
    }
    
    public int getId(){
        return this.studentId;
    }
    
    public static void main(String[] args){
        Student s = new Student();
        int id = s.setId(2018);//后续解释
    }
}

解释:调用setId方法时,除了传递"2018"参数外,还隐式的传递了"s"作为参数,在方法中的this关键字即表示该参数。

  • static修饰的方法则不需要针对于某些对象进行操作,其运算结果仅仅与输入的参数有关,调用时直接用类名引用
double d = Math.sqrt(3.0*3.0+4.0*4.0);

注意

  • 该方法在调用时,没有隐式的传递对象引用,因此在static方法中不可以使用this关键字
  • 由于static在调用时没有具体的对象,因此在static方法中不能对于非static成员(对象成员)进行访问
③static块:
  • 属于类的代码块,在类加载期间执行的代码块,只执行一次,可以用在软件中加载静态资源。
static{
	//语句
}
④static final常量:
  • static final修饰的成员变量称为常量,必须声明的同时进行初始化不可以被改变
  • static final常量会在编译期被替换
class Data{
    public static final int Max = 100;
}

class Test{
    public static void main(String[] args){
        System.out.println(Data.Max);
        //代码编译时,会替换成:System.out.println(100);
    }
}

补充:static final常量Data.Max会在编译时被替换成其常量值(100),在运行Test类时,Data类不需要被载入。


十三、抽象方法和抽象类:
①抽象方法和抽象类:
  • 由abstract修饰的方法为抽象方法,抽象方法只有方法的定义,没有方法体的实现,用一个分号结尾。
  • 一个类中如果包含抽象方法,该类应该用abstract关键字声明为抽象类。
  • 如果一个类继承了抽象类,必须重写其抽象方法,除非该类也声明为抽象类。
abstract class Person{
    int age;//抽象类中也可以包含变量
    abstract void Print();
}

class Teacher extends Person{
    String name;

    Teacher(String name,int age){
        this.name = name;
        this.age = age;
    }

    @Override
    void Print() {
        System.out.println("名字是:"+name);
        System.out.println("年龄是:"+age);
    }
    
    public static void main(String[] args) {
        Teacher t = new Teacher("Tom",12);
        t.Print();
    }
}
②抽象类不可以实例化:
  • 抽象类不可以实例化;例如:Person是一个抽象类,则如下代码是错误的:
Person p = new Person();
  • 即使一个类中没有抽象方法,也可以将其定义为抽象类,但是同样的,该类不可以实例化。
  • abstract和final关键字不可以同时用于修饰一个类,因为final关键字使得类不可继承,而abstract修饰的类如果不能继承将毫无意义。
③抽象类的意义:
  • 为其子类提供了一个公共的类型。
  • 封装子类中的重复内容(成员方法和变量)。
  • 定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的。

十四、成员内部类和匿名内部类:
①定义创建成员内部类:
  • 一个类可以定义在另一个类的内部,定义在内部的类称之为内部类,其所在的类称之为外部类。
  • 内部类的对象通常是在外部类中创建的;内部类通常只服务于外部类,对外不具备可见性
  • 内部类中可以直接访问外部类的成员(包括私有的)内部类中有一个隐式的引用指向了创建它的外部类对象
外部类名.this
class Outer{
	private int time;
	private Inner inner;
	
	Outer(int time){
		this.time = time;
		inner = new Inner();
		inner.timeInc();
	}
	
	public void Print(){
		System.out.println(time);
	}
	
	class Inner{
		public void timeInc(){
			time++;
		}
	}
	
	public static void main(String[] args){
		Outer outer = new Outer(100);
		outer.Print();
	}
}
//output:
//101

解释:在Outer构造方法中创建的Inner对象会有一个隐式的引用指向创建它的Outer对象,调用Inner对象的timeInc方法,即会对Outer的time属性进行操作。

②定义匿名内部类:
  • 如果在一段程序中需要创建一个类的对象(通常这个类需要实现某个接口或者继承某个类),而且对象创建之后,这个类的价值就不存在了,那么这个类可以不必命名,称其为匿名内部类

image-20210708223652699

补充:在jdk1.8之前,匿名内部类中若想访问外面的变量,则该变量必须是final

//抽象类,作为匿名内部类的父类进行创建
abstract class Worker{
    abstract public void Show();
    abstract public void SetString(String s);
}

public class Test{
    public static void main(String[] args) {
        String name = "Tom";
        Worker w = new Worker() {
            String course;
            public void SetString(String s) {
                this.course = s;
            }
            public void Show() {
                System.out.println(course);
                System.out.println(name);
    			//这里可以直接用,仅仅限于JDK1.8及之后,之前版本要用的话,得加final。
            }
        };
        w.SetString("Hello World!");
        w.Show();
    }
}

十五、接口:
①定义一个接口:
  • 接口可以看作是特殊的抽象类;即只包含有抽象方法的抽象类(抽象类中可以包含普通方法,也可包含抽象方法)。

image-20210708225347118

②实现接口:
  • 与继承不同,一个类可以实现多个接口,实现的接口直接用逗号隔开。当然,该类需要实现这些接口中定义的所有方法。
  • 一个类可以通过implements关键字实现接口;一个类实现了某个接口之后必须实现该接口中定义的所有办法。
class Test implements function1,function2,...{
	//实现接口中定义的所有方法。
}
  • 接口可以作为一种类型声明变量,一个接口类型的变量可以引用实现了该接口的类的对象;通过该变量可以调用该接口中定义的方法(具体的实现类提供了方法的实现)。
function1 f1 = new Test();

个人理解:就像《向上造型》那一篇笔记讲的原理类似,由父类来引用子类对象,但是在使用的时候会有所限制。

③接口的继承:
  • 接口间可以存在继承关系,一个接口可以通过extends关键字继承另一个接口。子接口继承父接口定义的所有方法
public interface Runner {
    public int Max = 100;
    public abstract void run();
}

public interface Function extends Runner{
    public void function();
}

class implements Function{
    //Test必须实现Runner接口和Function接口中的所有方法!
}
④接口、父类的设计规则:
  • 将派生类所共有的属性和行为,集中到超类(父类)中。
  • 派生类的行为都一样时,设计为普通方法;派生类行为都不一样时,设计为抽象方法。
  • 将部分派生类所共有的行为,抽象到接口中;接口是对继承单根性的扩展,间接实现了“多继承”。

十六、多态:
①多态的意义:
  • 同一类型的引用指向不同对象时会有不同的实现;同一对象造型成不同的类型时,也会有不同的功能
/**
 * 用于一些代码的测试
 * @author YiWen Wan
 */

abstract class Person{
    abstract public void Print();
}

class Teacher extends Person{
    @Override
    public void Print() {
        System.out.println("这是一个老师!");
    }
}

class Worker extends Person{
    @Override
    public void Print() {
        System.out.println("这是一个工人!");
    }
}

class Student extends Person{
    @Override
    public void Print() {
        System.out.println("这是一个学生!");
    }
    public String getName(){
        return "Tom";
    }
}

class ElementarySchoolStudent extends Student{
    @Override
    public void Print() {
        System.out.println("这是一个小学生!");
    }
    public double getScore(){
        return 100;
    }
}

class MiddleSchoolStudent extends ElementarySchoolStudent{
    @Override
    public void Print() {
        System.out.println("这是一个中学生!");
    }
    public String getSubject(){
        return "理科";
    }
}

class CollegeStudent extends MiddleSchoolStudent{
    @Override
    public void Print() {
        System.out.println("这是一个大学生!");
    }
    public String getMajor(){
        return "计算机科学与技术";
    }
}

public class Test{
    public static void main(String[] args) {
        //同一类型的引用指向不同的对象
        Person p1 = new Teacher();
        p1.Print();
        Person p2 = new Worker();
        p2.Print();
        Person p3 = new Student();
        p3.Print();

        System.out.println("分隔线*******************");
        //同一个对象造型成不同类型
        CollegeStudent collegeStudent = new CollegeStudent();
        System.out.println(collegeStudent.getMajor());

        MiddleSchoolStudent middleSchoolStudent = collegeStudent;
        System.out.println(middleSchoolStudent.getSubject());

        ElementarySchoolStudent elementarySchoolStudent = collegeStudent;
        System.out.println(elementarySchoolStudent.getScore());
        
        Student student = collegeStudent;
        System.out.println(student.getName());        ;
    }
}
//output:
//这是一个老师!
//这是一个工人!
//这是一个学生!
//分隔线*******************
//计算机科学与技术
//理科
//100.0
//Tom
②向上造型:
  • 一个类的对象可以向上造型的类型有:

image-20210709084251035

  • Java编译器根据引用的类型检查调用方法是否匹配。
③强转类型:
  • 使用规则

    • 可以通过强制转换将超类类型变量转换为派生类型变量,前提是该变量指向的对象确实是该派生类类型

    个人理解:相当于中间转了一道,先是一个超类(父类)变量接收一个派生类的对象,然后又将该超类(父类)变量又强制转换回派生类。

    • 也可以通过强制转换将变量转换为某种接口类型,前提是该变量指向的对象确实实现了该接口
  • 如果在强制转换过程中出现了违背上述的两个前提,将会抛出ClassCastException异常。

/**
 * 用于一些代码的测试
 * @author YiWen Wan
 */
interface Printer{
    public abstract void Print();
}

class Student implements Printer{
    private int age;
    private String name;

    Student(int age,String name){
        this.age = age;
        this.name = name;
    }

    @Override
    public void Print() {
        System.out.println("姓名:"+this.name+" 年龄:"+this.age+"!");
    }
}

public class Test extends Student{
    Test(int age, String name) {
        super(age, name);
    }

    public static void main(String[] args) {
        Student s = new Test(18,"Tom");
        s.Print();
        Test t = (Test) s;

        Printer printer = t;
        Test t1 = (Test) printer;
    }
}
//output:
//姓名:Tom 年龄:18!
④instanceof关键字:
  • 在强制转型中,为了避免出现ClassCastException,可以通过instanceof关键字判断某个引用指向的对象是否为指定类型
/**
 * 用于一些代码的测试
 * @author YiWen Wan
 */

abstract class Person{
    abstract public void Print();
}

class Teacher extends Person{
    @Override
    public void Print() {
        System.out.println();
        System.out.println("这是一个老师!");
    }
}

class HeadMater extends Teacher{
    @Override
    public void Print() {
        super.Print();
        System.out.println("这也是一个校长!");
    }
}

public class Test{
    public static void main(String[] args) {
        Person p = new Teacher();
        p.Print();
        System.out.println(p instanceof Person);
        System.out.println(p instanceof Teacher);
        System.out.println(p instanceof HeadMater);
        
        Person h = new HeadMater();
        h.Print();
        System.out.println(h instanceof Person);
        System.out.println(h instanceof Teacher);
        System.out.println(h instanceof HeadMater);
    }
}

//output:
//这是一个老师!
//true
//true
//false
//
//这是一个老师!
//这也是一个校长!
//true
//true
//true
  • 在强制类型转换之后也不会改变其在instanceof显示的"属性"。
/**
 * 用于一些代码的测试
 * @author YiWen Wan
 */
abstract class Person{
    abstract public void Print();
}

class Teacher extends Person{
    @Override
    public void Print() {
        System.out.println("这是一个老师!");
    }
}

class HeadMater extends Teacher{
    @Override
    public void Print() {
        super.Print();
        System.out.println("这也是一个校长!");
    }
}
public class Test{
    public static void main(String[] args) {
        Person k = new HeadMater();
        Person h = (Teacher)k;//强制类型转换之后的实验!
        h.Print();
        System.out.println(h instanceof Person);
        System.out.println(h instanceof Teacher);
        System.out.println(h instanceof HeadMater);
    }
}
//output:
//这是一个老师!
//这也是一个校长!
//true
//true
//true
Ending……
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值