Skip to content

面向对象

(Class)和对象(Object)是面向对象两个核心元素。

INFO

类:具有相同特征的事物的抽象描述,是 抽象的、概念上的定义。

对象:由类派生出来的实在的个体,是具体的,因此也称为 实例(Instance)。

Class类

注意

  1. 一个源文件中只能有一个 public 类,但可以有多个非 public 类;
  2. 源文件中类的名称应该和源文件的名称保持一致。如源文件名为 Person.java,则源文件中的 public 类的类名应该是 Person。
java
public class Phone {
  String name;
  double price;

  public void call() {
    System.out.println("Calling");
  }
  
  public void sendMessage(String message) {
    System.out.println(message);
  }
}

一个类可以包含以下类型变量:

  • 局部变量:在方法、构造器、语法块中定义的变量被称为局部变量。它的生命周期随着函数的销毁而销毁;
  • 成员变量:定义在类中、方法体外的变量。这中变量在创建对象时就会初始化,并且可以被类中的方法调用;
  • 类变量:类变量也声明在类中,方法体之外,但必须使用 static 修饰;

构造方法

每个类都有构造方法,如果没有显示指定构造方法,Java 编译器会自动为该类提供一个默认构造方法。

在创建对象时,可以提供一个或多个构造方法,构造方法名称必须与类名称相同。

java
public class Person {
  public Person() {
  }

  public Person(String name) {
  }
}

可变形参

可变参数,允许一个方法接受可变数量的参数。

可变参数使用省略号 ... 来定义,使得我们可以在调用方法时传递不同数量的参数,而不需要为每种情况创建不同的方法重载。

注意

  1. 可变参数必须声明在方法参数的最后面;
  2. 可变参数在一个方法中只能出现一次;
java
public class ArguTest {
  public static void main(String[] args) {
    ArguTest test = new ArguTest();
    test.add();
    test.add(1);
    test.add(1, 2);
  }

  public void add(int... nums) {
    System.out.println("arguments");
  }

  // 可变参数必须声明在方法参数的最后面
  public void add(int i, int... nums) {
    System.out.println("many arguments");
  }
}

🔔 思考:可变参数的方法 和 明确参数个数的方法,会优先调用谁呢? 答案是优先调用明确参数个数的方法。

下面的示例中,add() 方法构成了重载,并且按照参数个数调用指定的方法。

java
public class ArguTest {
  public static void main(String[] args) {
    ArguTest test = new ArguTest();
    test.add();
    test.add(1);
    test.add(1, 2);
  }

  public void add(int... nums) {
    System.out.println("arguments");
  }

  public void add(int a) {
    System.out.println("a");
  }

  public void add(int a, int b) {
    System.out.println("a b");
  }
}

递归方法

递归 就是方法自己调用自己。

注意

  1. 递归调用会占用大量的系统堆栈,内存消耗多,因此在递归调用层次多时效率不如循环,所以要谨慎使用递归;
  2. 在要求高性能的情况下,尽量避免使用递归,考虑使用循环迭代;

递归方法可以分为直接递归和间接递归。

java
// 直接递归
public void method() {
  System.out.println("method()...");
  method();
}

// 间接递归
public void A() {
  B();
}

public void B() {
  C();
}

public void C() {
  A();
}

案例一:计算 1 ~ 100 的加法和

java
public int getSum1(int num) {
  if (num == 1) {
    return 1;
  } else {
    return getSum1(num - 1) + num;
  }
}

案例二:计算斐波那契序列

java
// 计算斐波那契序列(该方法存在大量重复计算,可以进行优化)
public int f(int n) {
  if (n == 1 || n == 2) {
    return 1;
  } else {
    return f(n - 1) + f(n - 2);
  }
}

Object对象

在 java 中,Object 对象是顶级的一个类,其他 Class 类和 Array数组都继承自 Object 对象。

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

  p.setName("王一博");
  System.out.println(p.getName()); //王一博

  p.eat(); //人吃饭饭...

  if (p instanceof Object) {
    System.out.println("Person3 is Object"); //Person3 is Object
  }
}

class Person {
  private String name;

  public String getName() {
    return name;
  }

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

  public void eat() {
    System.out.println("人吃饭饭...");
  }
}

equals()

equals() 方法用于 比较两个 引用数据类型 的值是否相等

注意

如果自定义的 Class 类没有重写 equals() 方法的话,会默认调用 Object 类中的 equals() ,这时候是使用 == 进行比较的,是比较地址值是否相同。

java
public static void main(String[] args) {
  User user1 = new User("王一博", 23);
  User user2 = new User("王一博", 23);

  System.out.println(user1.equals(user2)); //false
}

class User {
  private String name;
  private int age;

  public User(String name, int age) {
    this.age = age;
    this.name = name;
  }
}
java
public static void main(String[] args) {
  User user1 = new User("王一博", 23);
  User user2 = new User("王一博", 23);

  System.out.println(user1.equals(user2)); //true
}

class User {
  private String name;
  private int age;

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

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof User user)) return false;
    return age == user.age && Objects.equals(name, user.name);
  }
}

面试题:java 中 == 和 equals() 有什么区别?

  1. == 在基本数据类型和引用数据类型中都可以使用,在基本数据类型比较时,就是比较值是否相同,但在引用数据类型中比较时,是比较地址值是否相同;
  2. equals() 方法只能在引用数据类型中使用,并且需要我们手动重写 equals() 方法,否则默认调用 Object 中的 equals()。但是,String类型除外,因为它已经默认重写过了,它就是在比较内容值是否相同。

toString()

toString() 方法是 Object 类的一个方法,每个类继承自 Object,因此每个类都会有 toString() 方法。

它的主要作用是返回对象的字符串表示形式

java
public static void main(String[] args) {
  Person person = new Person("王一博", 23);

  System.out.println(person.toString()); //Person{name='王一博', age=23}
  System.out.println(person); //Person{name='王一博', age=23}
}

class Person {
  private String name;
  private int age;

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

  @Override
  public String toString() {
    return "Person{" +
      "name='" + name + '\'' +
      ", age=" + age +
      '}';
  }
}

clone()

Clone() 方法用于克隆一个对象,克隆的对象和原来对象是两个不同的地址值,二者不会相互影响。

java
class Person implements Cloneable {
  private String name;

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

  public String getName() {
    return name;
  }

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

  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}
java
public static void main(String[] args) {
  Person p = new Person("王一博");

  try {
    Person pClone = (Person) p.clone();

    System.out.println(p);      //com.atguigu07.object.Person@b4c966a
    System.out.println(pClone); //com.atguigu07.object.Person@2f4d3709

    p.setName("陈伟霆");
    System.out.println(p.getName());      //陈伟霆
    System.out.println(pClone.getName()); //王一博

  } catch (CloneNotSupportedException e) {
    throw new RuntimeException(e);
  }
}

finalize() [已过时]

finalize() 方法用于当一个对象被垃圾回收时,可以自定义操作。但在 java9 之后,就被标识为过时了,现在我们无需手动垃圾回收,了解即可。

java
class Person {
  private String name;

  @Override
  protected void finalize() throws Throwable {
    System.out.println("垃圾被回收");
  }
}
java
public static void main(String[] args) {
  Person1 p = new Person1();
  //p对象已经没有了引用,可以被垃圾回收
  p = null;
  //手动垃圾回收
  System.gc();

  try {
    Thread.sleep(1000);
  } catch (InterruptedException e) {
    throw new RuntimeException(e);
  }
}

Released under the MIT License.