最近懒得要死,很久之前学过的,现在才整理出来。

Java是一门面向对象的编程语言。如果现在自己没有对象,就new一个。

面向对象的基本概念,包括:

  • 实例
  • 方法

面向对象的实现方式,包括:

  • 继承
  • 多态

Java语言本身提供的机制,包括:

  • package
  • classpath
  • jar

以及Java标准库提供的核心类,包括:

  • 字符串
  • 包装类型
  • JavaBean
  • 枚举
  • 常用工具类

面向对象基础

class是一种对象模版,它定义了如何创建实例,因此,class本身就是一种数据类型,而instance是对象实例,instance是根据class创建的实例,可以创建多个instance,每个instance类型相同,但各自属性可能不相同。

定义class

class Book {
    public String name;
    public String author;
    public String isbn;
    public double price;
}

一个class可以包含多个字段,每个字段描述一个类的特征,就如上面的book类,定义了四个字段,前三个是String类型的字段,命名为name author isbn,最后一个是int类型的字段,命名为price,所以通过类,把一组数据汇集到一个对象上,实现数据封装。

创建实例

创建对象实例必须用new操作符,然后定义一个引用类型的变量来指向这个实例:

Person peng = new Person();

创建了一个Person类型的实例,并通过变量peng指向它。

其中的Person peng是定义Person类型的变量peng,而后面的new Person是创建的实例。

访问实例变量通过变量.字段

peng.age = 19;//对字段age赋值
System.out.println(peng.age);//访问字段age

注意:

一个Java源文件可以包含多个类的定义,但只能定义一个public类,且public类名必须与文件名一致。如果要定义多个public类,必须拆到多个Java源文件中。

方法

如果我们只是简单地将字段用public暴露在外面,会破坏封装性,所以通过private修饰field,拒绝外部访问,同时,我们还要使用方法(method)来让外部代码可以间接修改field

public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        ming.setName("Xiao Ming"); // 设置name
        ming.setAge(12); // 设置age
        System.out.println(ming.getName() + ", " + ming.getAge());
    }
}

class Person {
    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;
    }
}

这里外部代码通过调用方法setName()setAge()来间接修改private字段,这样我们在方法内部就可以检查传入的参数的正确与否。

调用方法的语法是实例变量.方法名(参数);

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

定义方法

定义方法的语法是:

修饰符 方法返回类型 方法名(方法参数列表) {
    若干方法语句;
    return 方法返回值;
}

方法返回值通过return语句实现,如果没有返回值,返回类型设置为void,可以省略return

方法参数

调用方法时,必须严格按照参数的定义一一传递。

例如:

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

在调用setNameAndAge()方法时,第一个参数为String,第二个为int

Person peng = new Person();
peng.setNameAndAge("Bzp",19)
可变参数

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

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

输出

Homer Simpson
Bart Simpson

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

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

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

输出

Bob
Bob

原因:String为不可变类,会用一个新的地址引用。

java

基础数据类型,传递的是调用方 值的拷贝。

引用数据类型,传递是对象的引用。当参数为不可变对象(如String对象)时,调用方修改变量值只是将自己作用域下的变量指向了新的对象,不影响接收方的参数变量;当参数为可变对象(如Array对象)时,调用方修改变量值相当于自己作用域下的变量所指向的对象的值,影响接收方的参数变量。

python

都是传递的对象的引用(python中一切皆对象),和java的引用数据类型参数传递原理相同。

private方法

private字段一样,private方法不允许外部调用,定义private方法的理由是内部方法是可以调用private方法的。

public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        ming.setBirth(2008);
        System.out.println(ming.getAge());
    }
}

class Person {
    private String name;
    private int birth;

    public void setBirth(int birth) {
        this.birth = birth;
    }

    public int getAge() {
        return calcAge(2019); // 调用private方法
    }

    // private方法:
    private int calcAge(int currentYear) {
        return currentYear - this.birth;
    }
}

calcAge()是一个private方法,外部代码无法调用,但是,内部方法getAge()可以调用它。

方法可以封装一个类的对外接口,调用方不需要知道也不关心Person实例在内部到底有没有age字段。

this变量

在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。

如果没有命名冲突,可以省略this。例如:

class Person {
    private String name;

    public String getName() {
        return name; // 相当于this.name
    }
}

但是,如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this

class Person {
    private String name;

    public void setName(String name) {
        this.name = name; // 前面的this不可少,少了就变成局部变量name了
    }
}

构造方法

在创建对象实例时就把内部字段全部初始化为合适的值。

创建实例的时候,实际上是通过构造方法来初始化实例的。

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都有构造方法,之前没有编写构造方法就可以调用new Person(),是因为编译器会自动为我们生成一个默认构造方法,没有参数,也没有执行语句,类似这样:

class Person {
    public Person() {
    }
}

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

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

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("Xiao Ming", 15); // 既可以调用带参数的构造方法
        Person p2 = new Person(); // 也可以调用无参数构造方法
    }
}

class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

在Java中,创建对象实例的时候,按照如下顺序进行初始化:

  1. 先初始化字段,例如,int age = 10;表示字段初始化为10double salary;表示字段默认初始化为0String name;表示引用类型字段默认初始化为null
  2. 执行构造方法的代码进行初始化。
多构造方法

Java可以定义多个构造方法,编译器通过构造方法的参数数量、位置和类型自动区分。

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

方法重载

在一个类中,我们可以定义多个方法。但是有时一系列的方法,功能都是类似的,仅参数不同,那么,可以把这一组方法名做成同名方法。这种方法名相同,但各自的参数不同,称为方法重载(Overload)。

例如,String类提供了多个重载方法indexOf(),可以查找子串:

  • int indexOf(int ch):根据字符的Unicode码查找;
  • int indexOf(String str):根据字符串查找;
  • int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;
  • int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。

继承

继承是面向对象编程中非常强大的一种机制,可以复用代码。

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) { … }
}

注意:子类自动获得了父类的所有字段,严禁定义与父类重名的字段!

在OOP的术语中,我们把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修饰的字段可以被子类访问:

class Person {
    protected String name;
    protected int age;
}

class Student extends Person {
    public String hello() {
        return "Hello, " + name; // OK!
    }
}

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

super

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

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

在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();

如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。

不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。

最后修改:2021 年 08 月 06 日 11 : 33 AM
如果觉得我的文章对你有用,请随意赞赏