(笔记)java面向对象

在这里插入图片描述
在这里插入图片描述

1.方法

参数绑定

  • 基本类型参数绑定
public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        int n = 15; // n的值为15
        p.setAge(n); // 传入n的值
        System.out.println(p.getAge()); // 15
        n = 20; // n的值改为20
        System.out.println(p.getAge()); // 15还是20?
    }
}

class Person {
    private int age;

    public int getAge() {
        return this.age;
    }

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

在这里插入图片描述

结论:基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。

  • 引用类型参数绑定

    public class Main {
        public static void main(String[] args) {
            Person p = new Person();
            String[] fullname = new String[] { "Homer", "Simpson" };
            p.setName(fullname); // 传入fullname数组
            System.out.println(p.getName()); // "Homer Simpson"
            fullname[0] = "Bart"; // fullname数组的第一个元素修改为"Bart"
            System.out.println(p.getName()); // "Homer Simpson"还是"Bart Simpson"?
        }
    }
    
    class Person {
        private String[] name;
    
        public String getName() {
            return this.name[0] + " " + this.name[1];
        }
    
        public void setName(String[] name) {
            this.name = name;
        }
    }
    

在这里插入图片描述

结论:引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方(因为指向同一个对象嘛)。

  • 思考:下面这段代码为何输出是一样的?

    public class Main {
        public static void main(String[] args) {
            Person p = new Person();
            String bob = "Bob";
            p.setName(bob); // 传入bob变量
            System.out.println(p.getName()); // "Bob"
            bob = "Alice"; // bob改名为Alice
            System.out.println(p.getName()); // "Bob"还是"Alice"?
        }
    }
    
    class Person {
        private String name;
    
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    String的值是不可变的 ,对String 重新赋值的时候,会重新创建一个bob的引用

    当 bob = "Alice"时,会在内存中新开辟一个空间,bob的指向该变了。但是name属性还是指向原来的内存空间 还是Bob

2.构造方法

是不是任何class都有构造方法?是的。

那前面我们并没有为Person类编写构造方法,为什么可以调用new Person()

原因是如果一个类没有定义构造方法,编译器会自动为我们生成一个默认构造方法,它没有参数,也没有执行语句,类似这样:

class Person {
    public Person() {
    }
}

要特别注意的是,如果我们自定义了一个构造方法,那么,编译器就不再自动创建默认构造方法

3.重载

在一个类中,我们可以定义多个方法。如果有一系列方法,它们的功能都是类似的,只有参数有所不同,那么,可以把这一组方法名做成同名方法。例如,在Hello类中,定义多个hello()方法:

class Hello {
    public void hello() {
        System.out.println("Hello, world!");
    }

    public void hello(String name) {
        System.out.println("Hello, " + name + "!");
    }

    public void hello(String name, int age) {
        if (age < 18) {
            System.out.println("Hi, " + name + "!");
        } else {
            System.out.println("Hello, " + name + "!");
        }
    }
}

4.继承

继承有个特点,就是子类无法访问父类的private字段或者private方法。

这使得继承的作用被削弱了。为了让子类可以访问父类的字段,我们需要把private改为protected。用protected修饰的字段可以被子类访问

4.1 super

super关键字表示父类(超类)

子类在创建时,必须先调用父类的构造方法

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Xiao Ming", 12, 89);
    }
}

class Person {
    protected String name;
    protected int age;

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

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age); // 调用父类的构造方法Person(String, int)
        this.score = score;
    }
}

4.2 阻止继承

正常情况下,只要某个class没有final修饰符,那么任何类都可以从该class继承。

4.3 转型

这种把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)

把一个父类类型强制转型为子类类型,就是向下转型(downcasting)

为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型:

5. 多态

在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)。

Override和Overload不同的是,如果方法名一样(参数不一样),就是Overload,Overload方法是一个新方法;如果方法名相同(参数也一样),并且返回值也相同,就是Override

在这里插入图片描述

加上@Override可以让编译器帮助检查是否进行了正确的覆写。

6.抽象类

如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法:

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

如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。

因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。

使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类:

Person p = new Person(); // 编译错误

因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错

7.接口

在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。

如果一个抽象类没有字段,所有方法全部都是抽象方法:

abstract class Person {
    public abstract void run();
    public abstract String getName();
}

就可以把该抽象类改写为接口:interface

在Java中,使用interface可以声明一个接口:

interface Person {
    void run();
    String getName();
}

default方法

public class Main {
    public static void main(String[] args) {
        Person p = new Student("Xiao Ming");
        p.run();
    }
}

interface Person {
    String getName();
    default void run() {
        System.out.println(getName() + " run");
    }
}

class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。

8. 静态字段和静态方法

8.1 静态字段

public class Main {
    public static void main(String[] args) {
        Person ming = new Person("Xiao Ming", 12);
        Person hong = new Person("Xiao Hong", 15);
        ming.number = 88;
        System.out.println(hong.number);
        hong.number = 99;
        System.out.println(ming.number);
    }
}

class Person {
    public String name;
    public int age;

    public static int number;

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

在这里插入图片描述

虽然实例可以访问静态字段,但是它们指向的其实都是Person class的静态字段。所以,所有实例共享一个静态字段。

因此,不推荐用实例变量.静态字段去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段来访问静态对象。

推荐用类名来访问静态字段。可以把静态字段理解为描述class本身的字段(非实例字段)。对于上面的代码,更好的写法是

Person.number = 99;
System.out.println(Person.number);

8.2 静态方法

调用静态方法则不需要实例变量,通过类名就可以调用

public class Main {
    public static void main(String[] args) {
        Person.setNumber(99);
        System.out.println(Person.number);
    }
}

class Person {
    public static int number;

    public static void setNumber(int value) {
        number = value;
    }
}

因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段。

通过实例变量也可以调用静态方法,但这只是编译器自动帮我们把实例改写成类名而已

8.3接口的静态字段

因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型:

public interface Person {
    public static final int MALE = 1;
    public static final int FEMALE = 2;
}

实际上,因为interface的字段只能是public static final类型,所以我们可以把这些修饰符都去掉,上述代码可以简写为:

public interface Person {
    // 编译器会自动加上public statc final:
    int MALE = 1;
    int FEMALE = 2;
}

8.4静态代码块

静态代码块和非静态的区别

静态代码块,在虚拟机加载类的时候就会加载执行,而且只执行一次;
非静态代码块,在创建对象的时候(即new一个对象的时候)执行,每次创建对象都会执行一次

//class A 
package com.my.test;


class A {

    static {
        System.out.println("A1:父类静态代码区域");
    }

  
    

    {
        System.out.println("A2:父类非静态代码区域");
    }

    public A() {
        System.out.println("A3:父类构造器");
    }
}

//class B

package com.my.test;

public class B extends A {

    static {
        System.out.println("B1:子类静态代码区域");
    }

  
    

    {
        System.out.println("B2:子类非静态代码区域");
    }

    public B() {
        System.out.println("B3:子类构造器");
    }
}


// 测试类
package com.my.test;

public class Test {
    public static void main(String[] args) {
        B b1 = new B();
        System.out.println("====");
        B b2 = new B();
        

    }

}
A1:父类静态代码区域
B1:子类静态代码区域
A2:父类非静态代码区域
A3:父类构造器
B2:子类非静态代码区域
B3:子类构造器
====
A2:父类非静态代码区域
A3:父类构造器
B2:子类非静态代码区域
B3:子类构造器
静态代码块和静态方法的区别

两者的区别就是:静态代码块是自动执行的;

静态方法是被调用的时候才执行的.

9. 作用域

包作用域是指一个类允许访问同一个package的没有publicprivate修饰的class,以及没有publicprotectedprivate修饰的字段和方法

public:

​ 定义为publicclassinterface可以被其他任何类访问

​ 定义为publicfieldmethod可以被其他类访问,前提是首先有访问class的权限

private:定义为privatefieldmethod无法被其他类访问

protected:作用于继承关系。定义为protected的字段和方法可以被子类访问,以及子类的子类(同一包下也可)

final:

​ 用final修饰class可以阻止被继承

​ 用final修饰method可以阻止被子类覆写

​ 用final修饰field可以阻止被重新赋值

​ 用final修饰局部变量可以阻止被重新赋值

10.内部类

它被定义在另一个类的内部,所以称为内部类(Nested Class)。

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested"); // 实例化一个Outer
        Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
        inner.hello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    class Inner {
        void hello() {
            System.out.println("Hello, " + Outer.this.name);
        }
    }
}

要实例化一个Inner,我们必须首先创建一个Outer的实例,然后,调用Outer实例的new来创建Inner实例

Inner Class和普通Class相比,除了能引用Outer实例外,还有一个额外的“特权”,就是可以修改Outer Class的private字段,因为Inner Class的作用域在Outer Class内部,所以能访问Outer Class的private字段和方法。

Anonymous Class(匿名类)

Runnable r = new Runnable() {
    // 实现必要的抽象方法...
};

Runnable本身是接口,接口是不能实例化的,所以这里实际上是定义了一个实现了Runnable接口的匿名类,并且通过new实例化该匿名类,然后转型为Runnable,例如

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested");
        outer.asyncHello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    void asyncHello() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello, " + Outer.this.name);
            }
        };
        new Thread(r).start();
    }
}

Static Nested Class(静态内部类)

public class Main {
    public static void main(String[] args) {
        Outer.StaticNested sn = new Outer.StaticNested();
        sn.hello();
    }
}

class Outer {
    private static String NAME = "OUTER";

    private String name;

    Outer(String name) {
        this.name = name;
    }

    static class StaticNested {
        void hello() {
            System.out.println("Hello, " + Outer.NAME);
        }
    }
}

Static Nested Class(静态内部类)

public class Main {
    public static void main(String[] args) {
        Outer.StaticNested sn = new Outer.StaticNested();
        sn.hello();
    }
}

class Outer {
    private static String NAME = "OUTER";

    private String name;

    Outer(String name) {
        this.name = name;
    }

    static class StaticNested {
        void hello() {
            System.out.println("Hello, " + Outer.NAME);
        }
    }
}

static修饰的内部类和Inner Class有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this,但它可以访问Outerprivate静态字段和静态方法。如果把StaticNested移到Outer之外,就失去了访问private的权限。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值