6.4 根父类

1、如何理解根父类是object这句话?

(1)所有类都直接或间接的继承Object(字面解释)

(2)Object类型的变量,可以接收任意类型的对象

(3)Object类型的数组,可以任意类型的元素

(4)规定Object[]类型的数组,可以接收任意的对象数组

(5)规定Object[]类型的数组,不可以接收元素是基本数据类型的一维数组

(6)Object类的所有方法(一共11个),在任意类型的对象中都可以找到。

public class Demo {
}
public class TestObject {
    public static void main(String[] args) {
        Object obj1 = "hello";
        Object obj2 = 1;//这里1是Integer的对象
        Object obj3 = new Scanner(System.in);

        Object[] arr1 = new String[5];
        Object[] arr2 = new Date[5];
//        Object[] arr3 = new int[5];//错误,int不是引用数据类型


        TestObject t = new TestObject();
        t.method("hello");
        t.method(1);
        t.method(new Scanner(System.in));

        String[] strings = new String[5];
        t.fun(strings);

        int[] nums = new int[5];
//        t.fun(nums);//错误,因为Object[] arr形参只能接收对象数组,不能接收基本数据类型的数组

        Demo d = new Demo();
        System.out.println(d.toString());//从Object类继承的
        System.out.println(d.getClass());//从Object类继承的
    }

    public void method(Object obj){
        //.....
    }

    public void fun(Object[] arr){
        ///...
    }
    /*public void fun(int[] arr){
        ///...
    }
    public void fun(double[] arr){

    }*/
}

Java学习笔记(九)_成员变量

6.5 构造器

6.5.1 构造器的作用和特点

成员:  

(1)成员变量(*****)

(2)成员方法(*****)

(3)构造器(*****)

(4)代码块(**)

(5)成员内部类(**)


1、什么是构造器?它的作用是什么?

构造器的作用是创建对象,并且为对象的实例变量初始化。

2、构造器的特点

(1)构造器的名字必须和类名完全一致,包括大小写。

(2)构造器没有返回值类型,一旦写了返回值类型,它就变成普通方法了。

(3)构造器的修饰符只允许有public,protected,缺省,private四个之一

(4)所有类都有构造器,

如果没有编写构造器,编译器会给你自动加一个默认的无参构造。这里默认的意思是,权限修饰符和class前面默认一致。

(5)程序员也可以手动编写构造器,

一旦我们编写了构造器,编译器就不再给你添加默认的无参构造了。

(6)构造器可以重载,一个类可以有多个构造器。

(7)当声明子类时,在子类的构造器中,默认会调用父类的无参构造。

如果父类没有无参构造,又没有手动调用父类的有参构造,那么编译报错。


结论:

   要么让它默认调用父类的无参构造, 不写

   要么必须手动调用父类的有参构造,  super(实参列表);

   当然也可以手动调用父类的无参构造。 super();


   super(); 或 super(实参列表); 必须在子类构造器的首行。


建议大家:父类尽量保留无参构造。

public class Employee {
    private String name;
    private double salary;
    private int age;

    //如何快速声明构造器呢?快捷键是Alt + Insert


    public Employee() {
    }

    public Employee(String name, double salary, int age) {	 
        this.name = name;
        this.salary = salary;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", age=" + age +
                '}';
    }
}
public class Manager extends Employee {
    private double bonus;

    public Manager() {
        //super();
    }

    public Manager(String name, double salary, int age, double bonus) {
        super(name, salary, age);
        this.bonus = bonus;
    }
}
public class TestEmployee {
    public static void main(String[] args) {
        /*Employee e1 = new Employee();//无参构造
        Employee e2 = new Employee("张三",18000,23);

        System.out.println(e1);
        System.out.println(e2);

        e2.setAge(24);//可以修改对象的属性
        System.out.println(e2);*/
    }
}

Java学习笔记(九)_构造器_02

public class TestManager {
    public static void main(String[] args) {
        Manager manager = new Manager();//调用Manager类的无参构造创建对象
    }
}

6.5.2 调用当前类的其他构造器

3、同一个类中,构造器是否可以互相调用?

可以

调用形式:

(1)this();

(2)this(实参列表);

这两句代码和super(); 或 super(实参列表);一样,必须在构造器的首行。


最终结论:

   子类构造器首行如果没有写: this(); this(实参列表); super(); 或 super(实参列表);,默认就是super();

   如果手动写了它们中的任意一种,写谁就是谁。

public class Goods {
    private String title;//标题,名称
    private double price;//价格
    private double amount;//数量
    private boolean reback;//是否支持退货

    public Goods() {
       super();
    }

    public Goods(String title, double price) {
       this();
        this.title = title;
        this.price = price;
    }

    public Goods(String title, double price, double amount, boolean reback) {
        /*this.title = title;
        this.price = price;*/
        this(title, price);
        this.amount = amount;
        this.reback = reback;
    }
}

6.5.3 自动生成构造器

4、IDEA生成构造器的快捷键
(1)Alt + Insert,选择Constructor
(2)自定义快捷模板,用于生成无参构造

6.6 非静态代码块

类的成员:

(1)成员变量

(2)构造器(又叫构造方法)

(3)代码块(了解)

(4)成员方法

(5)内部类


1、代码块有两种形式:

(1)静态代码块

(2)非静态代码块


2、非静态代码块的作用

配合构造器在创建对象时,完成对象实例变量的初始化过程。


3、它的语法结构:

【修饰符】 class 类名{

   {

       非静态代码块

   }

}


4、执行特点

非静态代码块是在构造器调用时自动调用的。每new对象,就会执行一次非静态代码块。

先于构造器的代码执行。


5、需求:

(1)所有的构造器在new对象之前,都输出一句话:一个对象被创建了

(2)给a,b变量赋值,用随机数[0,100)值赋值


解决方案有两种:

(1)把构造器的公共代码放到非静态代码块中

(2)把公共代码放到某一个构造器里面(通常是无参构造)然后其他构造器通过this(); 或 this(实参列表);调用这个代码

public class Demo {
    public Demo(){
        System.out.println("Demo.Demo");
    }
    {
        System.out.println("非静态代码块");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Demo d1 = new Demo();
        Demo d2 = new Demo();
    }
}

Java学习笔记(九)_父类_03

public class MyClass {
    private int a;
    private int b;
    private String info;

    {
        System.out.println("一个对象被创建了");
        a = (int)(Math.random()*100);
        b = (int)(Math.random()*100);
    }
    
    public MyClass(){

    }

    public MyClass( String info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "MyClass{" +
                "a=" + a +
                ", b=" + b +
                ", info='" + info + '\'' +
                '}';
    }
}

6.7 实例初始化过程(面试题)

1、实例初始化过程:就是给对象的实例变量赋值的过程


2、刚才讲了3个操作都可以给实例变量赋初始值

(1)声明实例变量时,直接=值

(2)非静态代码块

(3)构造器


实际开发中都是选择构造器这一种方式比较多。

如果这3种方式同时存在,或者其中2种同时存在,那么实例变量的值最后以谁为准呢。

这就要求我们弄清楚它们的执行顺序,后面的会把前面的赋值给覆盖了。


3、实例初始化过程的顺序

(1)super();或super(实参列表);

(2)声明实例变量时,直接=值

(3)非静态代码块

(4)构造器其他代码(除了super();或super(实参列表);)


(1)最先执行的,然后(2)和(3)是按照代码编写的顺序执行,(4)在它们后面


4、编译器会把上面的4个部分的代码,组装成一个一个的<init>实例初始化方法。

你编写了你几个构造器,就有几个<init>实例初始化方法。

组装的顺序就是按照上面的(1),然后(2)和(3)是按照代码编写的顺序组装,(4)在它们后面。


init:initialize初始化

public class Demo {
    public Demo(int a) {
        this.a = a;
    }

    private int a = 1;
    {
        a = 2;
    }

    public Demo() {
    }

    @Override
    public String toString() {
        return "Demo{" +
                "a=" + a +
                '}';
    }
}

Java学习笔记(九)_成员变量_04

public class Father {
    {
        System.out.println("父类的非静态代码块");
    }
    public Father(){
        System.out.println("Father.Father");
    }
}
public class Son extends Father {
    {
        System.out.println("子类的非静态代码块");
    }
    public Son(){
        super();
        System.out.println("Son.Son");
    }
}
public class TestSon {
    public static void main(String[] args) {
        Father f = new Son();
    }
}

Java学习笔记(九)_成员变量_05

6.8 关键字整理

6.8.1 this和super

1、this代表什么意思?

当前对象


2、如何理解当前对象?

(1)构造器、非静态代码块中出现,this代表你正在new的这个对象

(2)方法体中出现,this代表调用方法的对象


3、this使用的形式

(1)this独立使用,可以用于输出等

(2)this()或this(实参列表)

表示调用当前类的其他构造器,而且必须在构造器的首行。

this():调用当前类的无参构造。

this(实参列表):调用当前类的有参构造。

   

(3)this.成员变量

A:当局部变量(通常是形参)与成员变量重名时,使用this.成员变量与局部变量进行区分。

B:没有重名问题,也可以使用this.成员变量,表示访问成员变量。


思考?this.成员变量是否一定是当前类声明的?

通常 this.成员变量指当前类声明的,但是它也可以是父类声明的。

要通过this.成员变量访问父类声明的成员变量,有条件:

A:父类声明的成员变量的权限修饰符,必须是在子类仍然可见 >private。

B:子类不能有和父类重名的成员变量


(4)this.成员方法

完全可以省略this.,加或不加this.都一样。

如果是this.后面是虚方法的话:

编译时:this.成员方法表示先从当前类查找匹配的方法,如果没有找到,继续从父类声明的方法列表中寻找匹配的方法。

运行时:要看this的运行时类型,如果this代表子类的对象,要看子类是否重写了该方法。


this:编译时类型,当前类类型,

    运行时类型,要看new的对象类型。


4、super的意思:代表父类声明的xx。

super引用任何父类的成员,都要求该成员在子类是可见的。


5、super的使用形式

(1)super()或super(实参列表)

表示匹配父类的无参构造或有参构造,必须在子类构造器的首行。

实际执行的是父类的<init>()或<init>(形参列表)的实例初始化方法。


(2)super.成员变量

当子类有和父类重名的成员变量,那么super.成员变量代表父类的,this.成员变量代表子类的。


this.成员变量先从当前类的成员变量列表开始找,如果找到,就是子类的,如果没找到,就去父类找。

super.成员变量,就是直接从父类成员变量列表找。


(3)super.成员方法

当子类重写了父类的方法时,要调用父类被重写方法,就用super.成员方法,

否则不需要用super.就可以直接调用从父类继承的方法。


6、总结

(1)构造器访问

super()或super(实参列表):一定代表父类的构造器,而且仅限于直接父类。

this()或this(实参列表):一定代表当前类的构造器,不会去别的类找。


(2)成员变量访问

this.成员变量:从当前类的成员变量列表开始找,如果找到,就是子类的,如果没找到,就去父类找。

super.成员变量:直接从父类成员变量列表找。


(3)方法调用

super.方法:一定从父类的成员方法列表找,找到谁就执行谁。

this.方法:

   如果是虚方法,

   编译时:this.成员方法表示先从当前类查找匹配的方法,如果没有找到,继续从父类声明的方法列表中寻找匹配的方法。

   运行时:要看this的运行时类型(new),如果this代表子类的对象,要看子类是否重写了该方法。


(4)this可以独立使用,super不允许独立使用。

6.8.2 native

1、native:本地的

2、native在Java中是一个修饰符,只能用来修饰方法。

3、用native修饰的方法,表示本地方法,它在.java文件中没有方法体。

【修饰符】 class Java类{

   【其他修饰符】 native 返回值类型 方法名(【形参列表】);

}

它的方法体在底层C或C++中。

4、Object类中

public native int hashCode();  返回对象的哈希值

public final native Class<?> getClass(); 返回对象的运行时类型


5、在Java中如何使用native修饰的方法呢?

完全不用管native的事,直接调用即可。

甚至子类也可以重写native修饰的方法。

6.8.3 final

1、final:最终的,不可更改的

2、final的用法:

(1)修饰类,即在class前面加final

表示这个类不能被继承,比喻太监类,断子绝孙类


我们见过的String,Math,System类。

往往final修饰的类都非常重要,它里面的所有方法都不允许重写,不允许扩展。


(2)修饰方法,即在方法的返回值类型前面加final

表示这个方法不能被子类重写,可以被子类继承。



(3)修饰变量,即在变量的数据类型前面加final

表示这个变量的值不能修改,是常量

final修饰的变量必须手动初始化,

A:形参:靠实参初始化

B:其他局部变量,必须手动赋值

C:成员变量,必须在非静态代码块、构造器、成员变量声明后面给它初始值


final修饰的成员变量没有set方法,在Java层面它是无法修改值的。

建议,final修饰的变量用大写。

public class Triangle {
    private final double a;
    private final double b ;
    private final double c ;

    public Triangle(double a, double b, double c) {
        if(a>0 && b>0 && c>0 && a+b>c && a+c>b && b+c>a) {
            this.a = a;
            this.b = b;
            this.c = c;
        }else{
            this.a = 0;
            this.b = 0;
            this.c = 0;
        }
    }

    public double getA() {
        return a;
    }

    public double getB() {
        return b;
    }

    public double getC() {
        return c;
    }

    @Override
    public String toString() {
        return "Triangle{" +
                "a=" + a +
                ", b=" + b +
                ", c=" + c +
                '}';
    }
}
public class TestFinal {
    public static void main(String[] args) {
       final int a = 1;
//       a = 2;//错误,final修饰的变量是常量,不能修改

        Triangle t = new Triangle(3,4,5);
        System.out.println(t);
    }
}

/*
final class Demo{

}
class SubDemo extends Demo{//报错,不允许继承final修饰的类

}*/

class MyClass{
    public final void method(){
        System.out.println("MyClass.method");
    }
}
class SubClass extends MyClass{
    //报错,不能重写final方法
 /*   public final void method(){
        System.out.println("MyClass.method");
    }*/
}