面向对象
类(Class)和对象(Object)是面向对象两个核心元素。
INFO
类:具有相同特征的事物的抽象描述,是 抽象的、概念上的定义。
对象:由类派生出来的实在的个体,是具体的,因此也称为 实例(Instance)。
Class类
注意
- 一个源文件中只能有一个 public 类,但可以有多个非 public 类;
- 源文件中类的名称应该和源文件的名称保持一致。如源文件名为 Person.java,则源文件中的 public 类的类名应该是 Person。
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 编译器会自动为该类提供一个默认构造方法。
在创建对象时,可以提供一个或多个构造方法,构造方法名称必须与类名称相同。
public class Person {
public Person() {
}
public Person(String name) {
}
}
可变形参
可变参数,允许一个方法接受可变数量的参数。
可变参数使用省略号 ...
来定义,使得我们可以在调用方法时传递不同数量的参数,而不需要为每种情况创建不同的方法重载。
注意
- 可变参数必须声明在方法参数的最后面;
- 可变参数在一个方法中只能出现一次;
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()
方法构成了重载,并且按照参数个数调用指定的方法。
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");
}
}
递归方法
递归 就是方法自己调用自己。
注意
- 递归调用会占用大量的系统堆栈,内存消耗多,因此在递归调用层次多时效率不如循环,所以要谨慎使用递归;
- 在要求高性能的情况下,尽量避免使用递归,考虑使用循环迭代;
递归方法可以分为直接递归和间接递归。
// 直接递归
public void method() {
System.out.println("method()...");
method();
}
// 间接递归
public void A() {
B();
}
public void B() {
C();
}
public void C() {
A();
}
案例一:计算 1 ~ 100 的加法和
public int getSum1(int num) {
if (num == 1) {
return 1;
} else {
return getSum1(num - 1) + num;
}
}
案例二:计算斐波那契序列
// 计算斐波那契序列(该方法存在大量重复计算,可以进行优化)
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 对象。
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() ,这时候是使用 == 进行比较的,是比较地址值是否相同。
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;
}
}
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() 有什么区别?
- == 在基本数据类型和引用数据类型中都可以使用,在基本数据类型比较时,就是比较值是否相同,但在引用数据类型中比较时,是比较地址值是否相同;
- equals() 方法只能在引用数据类型中使用,并且需要我们手动重写 equals() 方法,否则默认调用 Object 中的 equals()。但是,String类型除外,因为它已经默认重写过了,它就是在比较内容值是否相同。
toString()
toString()
方法是 Object 类的一个方法,每个类继承自 Object,因此每个类都会有 toString()
方法。
它的主要作用是返回对象的字符串表示形式
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()
方法用于克隆一个对象,克隆的对象和原来对象是两个不同的地址值,二者不会相互影响。
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();
}
}
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 之后,就被标识为过时了,现在我们无需手动垃圾回收,了解即可。
class Person {
private String name;
@Override
protected void finalize() throws Throwable {
System.out.println("垃圾被回收");
}
}
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);
}
}