Java基础回顾——面向对象编程

https://www.liaoxuefeng.com/wiki/1252599548343744/1255943520012800

面向对象编程Object-Oriented Programming,简称OOP,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方法

面向对象基本概念

实例
方法
面向对象的实现方式
继承
多态
Java语言提供的机制
package
classpath
jar
Java标准库提供的核心类
字符串
包装类型
JavaBean
枚举
常用工具类

面向对象基础

面向对象编程,就是通过对象的方式,把现实世界映射到计算机模型的一种编程方法

class和instance
class是一种对象模板,定义了如何创建实例
instance是对象实例,是根据class创建的实例,可以创建多个instance,每个instance类型相同,但各自的属性可能不同

class Person{
	// field
	public String name;
	public int age;
}
Person ming=new Person();
ming.name="Xiao Ming";
ming.age=18;
Person hong=new Person();
hong.name="Xiao Hong";
hong.age=19;

在这里插入图片描述

方法

class中直接把field用public暴露给外部可能会破坏封装性

直接操作filed,容易造成逻辑混乱,未来避免外部代码直接访问field,可以用private修饰field,拒绝外部访问

class Person{
	// field
	private String name;
	private int age;
}

修改之后,外部无法访问field,想要给field赋值需要使用方法(method)来让外部代码间接修改field:

class Person{
	// field
	private String name;
	private int age;
    public String getName() {
        return this.name;
    }

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

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

    public void setAge(int age) {
        if (age < 0 || age > 100) {
            throw new IllegalArgumentException("invalid age value");
        }
        this.age = age;
    }
}

一个类通过定义方法,就可以给外部代码暴露一些操作的接口,同时,内部自己保证逻辑一致性。

private方法
private方法不允许外部调用,但内部方法可以调用

this变量
方法内部,可以使用一个隐含的变量this,始终指向当前实例。因此,通过this.field就可以访问当前实例的字段。

方法参数
方法可以包含0个或任意个参数。方法参数用于接收传递给方法的变量值。调用方法时,必须严格按照参数的定义一一传递。

    public void setNameAndAge(String name, int age) {
        ...
    }

可变参数
可变参数用类型…定义,可变参数相当于数组类型

class Group {
    private String[] names;

    public void setNames(String... names) {
        this.names = names;
    }
}
Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames("Xiao Ming"); // 传入1个String
g.setNames(); // 传入0个String

参数绑定
调用方把参数传递给实例方时,调用时传递的值会按照参数位置一一绑定

// 基本类型
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? ---> 15
    }
}

class Person {
    private int age;

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

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

修改外部的局部变量n,不影响实例p的age字段,原因是setAge()方法获得的参数,复制了n的值,因此,p.age和局部变量n互不影响。

// 引用类型
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"?---->"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"?---->"Bob"
    }
}

class Person {
    private String name;

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

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

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

构造方法

在创建对象实例时就把内部字段初始化为合适的值?需要构造方法。

在创建Person实例的时候,一次性传入name和age

public class Main {
    public static void main(String[] args) {
        Person p = new Person("Xiao Ming", 15);
        System.out.println(p.getName());
        System.out.println(p.getAge());
    }
}

class Person {
    private String name;
    private int age;

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

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

和普通方法相比,构造方法没有返回值,也没有void,调用构造方法,必须用new操作符

默认构造方法

任何class都要构造方法

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

class Person {
    public Person() {
    }
}

如果自定义了一个构造方法,编译器就不再自动创建默认构造方法

如果既要能使用带参数的构造方法,又要保留不带参数的构造方法,要把两个方法都定义出来

多构造方法

可以定义多个构造方法,在通过new操作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分

class Person {
    private String name;
    private int age;

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

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

    public Person() {
    }
}

一个构造方法可以调用其他构造方法,这样便于代码复用,调用其他构造方法的语法是this(…)

class Person {
    private String name;
    private int age;

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

    public Person(String name) {
        this(name, 18); // 调用另一个构造方法Person(String, int)
    }

    public Person() {
        this("Unnamed"); // 调用另一个构造方法Person(String)
    }
}

方法重载

在一个类中,可以定义多个方法。如果有一系列方法,功能都是类似的,只有参数有所不同,那么,可以把一组方法名做成同名方法

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 + "!");
        }
    }
}

这种方法名相同,但各自的参数不同,称为方法重载(Overload)

方法重载的返回值类型通常都是相同的

继承

继承是面向对象编程中非常强大的一种机制,可以复用代码,Java使用extends关键字来实现继承

class Person {
    private String name;
    private int age;

    public String getName() {...}
    public void setName(String name) {...}
    public int getAge() {...}
    public void setAge(int age) {...}
}

class Student extends Person {
    // 不要重复name和age字段/方法,
    // 只需要定义新增score字段/方法:
    private int score;

    public int getScore() {}
    public void setScore(int score) {}
}

Person称为超类super class,父类parent class,基类base class,把student称为子类subclass,扩展类extended class

继承树
在Java中,没有明确写extends的类,编译器会自动加上extends Object。所以,任何类,除了Object,都会继承自某个类。

在这里插入图片描述

Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有Object特殊,它没有父类。

protected

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

protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问。

super
super关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName。

class Student extends Person {
    public String hello() {
        return "Hello, " + super.name;
    }
}

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

从Java 15开始,允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。

public sealed class Shape permits Rect, Circle, Triangle {
    ...
}

多态

class Person {
    public void run() {
        System.out.println("Person.run");
    }
}
class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

抽象类

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

定义抽象方法,类也要定义为抽象类

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

抽象类无法实例化,只能用于被继承,可以强迫子类实现其定义的抽象方法,否则编译会报错。抽象方法实际上相当于定义了“规范”。

接口

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

如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口:interface。

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

interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。

当一个具体的class去实现一个interface时,需要使用implements关键字。

class Student implements Person {
    private String name;

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

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

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

java中,一个类只能继承自另一个类,不能从多个类继承,但是,一个类可以实现多个interface

class Student implements Person, Hello { // 实现了两个interface
    ...
}

接口继承

interface Hello {
    void hello();
}

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

静态字段和静态方法

在一个class中定义的字段,称之为实例字段。实例字段的特点是,每个实例都有独立的字段,各个实例的同名字段互不影响。

还有一种字段,是用static修饰的字段,称为静态字段:static field。

实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。

class Person {
    public String name;
    public int age;
    // 定义静态字段number:
    public static int number;
}

Java中使用包package来解决名字冲突

一个类总属于某个包,类名只是一个简写,真正完整的类名是 包名.类名

包作用域
位于同一个包的类,可以访问包作用域的字段和方法。不用public、protected、private修饰的字段和方法就是包作用域。

作用域

public

定义为public的class、interface可以被其他任何类访问

private

定义为private的field、method无法被其他类访问
一个类内部的嵌套类拥有访问private的权限

public class Main {
    public static void main(String[] args) {
        Inner i = new Inner();
        i.hi();
    }

    // private方法:
    private static void hello() {
        System.out.println("private hello!");
    }

    // 静态内部类:
    static class Inner {
        public void hi() {
            Main.hello();
        }
    }
}

protected

protected作用于继承关系。定义为protected的字段可以被子类访问,以及子类的子类

package

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

final

用final修饰class可以阻止被继承;
final修饰的method可以阻止被子类覆写;
final修饰field可以阻止被重新赋值;
final修饰局部变量可以阻止被重新赋值;

内部类

1、Inner class

class Outer {
    class Inner {
        // 定义了一个Inner Class
    }
}
Outer.Inner inner = outer.new Inner();

2、Anonymous class

不需要在Outer Class中明确地定义这个Class,而是在方法内部,通过匿名类(Anonymous Class)来定义。

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();
    }
}

3、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,但它可以访问Outer的private静态字段和静态方法。如果把StaticNested移到Outer之外,就失去了访问private的权限。

Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Class的private访问权限;
Static Nested Class是独立类,但拥有Outer Class的private访问权限。

写在最后

这些抄一遍好像也记不住,,,,还是要定期回顾。。。。

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值