Skip to content

类和对象

类,是一个抽象笼统的概念,它用来描述对象的特征(属性)行为(方法)。

对象,是由类衍生出来的一个具体的个体,它拥有和类相同的特征和行为。

属性

定义属性的格式:修饰符 数据类型 属性名 [= 值]

作用:静态描述类的特征,如 name、age 等。

java
public class Person {
  // 全局变量 字符型 属性名为name
  public String name;
  public int age;
  public String sex;
}

方法

定义方法的格式:权限修饰符 [特征修饰符] 返回值类型 方法名(参数列表) [抛出异常] [{ 方法体 }]

作用:动态描述类的行为,如 eatRice 等。

常见的方法类型有四种:

  • 无参数无返回值
  • 无参数有返回值
  • 有参数无返回值
  • 有参数有返回值
java
public class Person {
	// 全局变量 字符型 属性名为name
	public String name;
	public int age;
	public String sex;

	// 无参数无返回值,void表示没有返回值
	public void eatRice() {
		System.out.println("吃了一碗米饭");
	}
	// 无参数有返回值
	public String saySomething() {
		return "说些啥";
	}
	// 有参数无返回值
	public void eat(String food) {
		System.out.println("吃了" + food);
	}
	// 有参数有返回值
	public String buyDrink(int money) {
		return money > 5 ? "红牛" : "矿泉水";
	}
  
	public static void main(String[] args) {
		Person p = new Person();
		p.name = "Tom";
		p.age = 15;
		p.sex = "男";
		System.out.println(p.name + "年龄" + p.age + ",性别" + p.sex);
		p.eatRice();
		String sth = p.saySomething();
		System.out.println(sth);
		p.eat("面条");
		String drink = p.buyDrink(10);
		System.out.println("买了" + drink);
	}
}

提示

每一个类中不是必须包含主方法的。

主方法不属于任何一个类,主方法属于虚拟机。

方法重写(Override)

方法重写,指的是在有继承关系的两个类中,子类中的方法可以与父类中方法的名称和参数完全相同,但方法的实现不同。

作用:子类可以根据需要重写(覆盖)父类的方法。

java
class Animal {
  public void move() {
    System.out.println("动物可以移动");
  }
}

class Dog extends Animal {
  public void move() {
    System.out.println("狗可以跑和走");
  }  
}

public class Test {
  public static void main(String[] args) {
    Animal a = new Animal();
    Dog d = new Dog();
    a.move(); // 动物可以移动
    d.move(); // 狗可以跑和走
  }
}

方法重载(Overload)

方法重载,指的是一个类中的一组方法,它们有相同的方法名,但不同的参数列表(不同在于参数的个数、类型、顺序)。

作用:方便开发者记忆,只需要记录一个方法名就能执行不同的操作。

java
public class TestOverload {
  public void test() {
    System.out.println("");
  }
  
  public void test(boolean b) {
    System.out.println("执行了test方法,参数类型为boolean,实参为" + b);
  }
  
  public void test(int i) {
    System.out.println("执行了test方法,参数类型为int,实参为" + i);
  }
  
  public void test(String s) {
    System.out.println("执行了test方法,参数类型为String,实参为" + s);
  }
  
  public static void main(String[] args) {
    TestOverload to = new TestOverload();
    to.test();
    to.test(true); // 执行了test方法,参数类型为boolean,实参为true
    to.test('a'); // 执行了test方法,参数类型为int,实参为97
    to.test("a"); // 执行了test方法,参数类型为String,实参为a
  }
}

方法执行的机制:

  • 执行方法的时候,首先会通过方法名找到对应的方法
  • 如果有多个方法名相同的方法,则通过参数列表找到对应的方法
  • 如果没有参数列表相同的的方法,则找一个能满足自动类型转换的参数列表的方法
  • 以上都找不到,则报错。

jdk1.5 版本之后,出现了一种新的参数列表写法:type... x

这个写法叫做动态参数列表,开发者可以动态传递 0 - ♾️ 个 type 类型的数据,x 的本质就是一个数组。

java
public class TestOverload {
  public void test(int... x) {
    for(int i:x) {
      System.out.println("执行了test方法,携带动态列表" + i);
    }
  }
// //上下两个方法本质是一样的
// public void test(int[] x) {
//  
//}
  
  public static void main(String[] args) {
    TestOverload to = new TestOverload();
    to.test(1,2,3);
    // 执行了test方法,携带动态列表1
    // 执行了test方法,携带动态列表2
    // 执行了test方法,携带动态列表3
  }
}

构造函数

构造函数本质上就是一个方法。

定义构造函数的格式:权限修饰符 与类名一致的方法名(参数列表) [抛出异常] [{ 方法体 }]

作用:创建一个当前类的对象。

特点:

  • 如果类中没有定义构造函数,那么 java 编译器会默认提供一个没有参数的构造函数
  • 构造函数也存在方法重载
java
public class Person {
  public String name;
  
  public Person() {}
  public Person(String name) {
    this.name = name;
  }
}

程序块

定义程序块的格式:{}

作用:程序块的作用跟普通方法一样。

特点:

  • 程序块会在构造方法执行前执行一次
  • 程序块没有重载的概念
  • 程序块可以有多个,会按顺序执行
java
public class Person {
  public String name;
  public int age;
  
  public Person() {}
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
  
  {
  	System.out.println("程序块执行了");
  }
  
  {
    System.out.println("第二个程序块执行了");
  }
  
  public static void main(String[] args) {
    Person p = new Person("Tom", 18);
    System.out.println(p.name + "的年龄是" + p.age);
    
    // output:
    // 程序块执行了
    // 第二个程序块执行了
    // Tom的年龄是18
  }
}

this 关键字

this 是一个关键字,指的是当前访问属性或执行方法的对象。

我们可以在类中的任何成员中访问它,通过this 关键字可以调用属性、方法。

在构造函数中,this 关键字可以表示为另一个构造函数,通过this()来执行,但必须要放在第一行执行。

java
public class Person {
  public String name;
  public int age;
  
  public Person() {
  	System.out.println("无参数列表的构造函数执行了");
  }
  public Person(String name, int age) {
    this(); // 必须在第一行
    System.out.println("有参数列表的构造函数执行了");
    this.name = name;
    this.age = age;
  }
  
  
  public static void main(String[] args) {
    Person p = new Person("Tom", 18);
    System.out.println(p.name + "的年龄是" + p.age);
    
    // output:
    // 无参数列表的构造函数执行了
    // 有参数列表的构造函数执行了
    // Tom的年龄是18
  }
}

类成员的加载顺序

存在继承关系的类的加载顺序如下:

  • 加载父类
  • 父类产生自己的静态空间(属性、方法、程序块)
  • 执行父类的静态程序块
  • 加载子类
  • 子类产生自己的静态空间(属性、方法、程序块)
  • 执行子类的静态程序块
  • 开辟对象空间
  • 加载父类的非静态成员(属性、方法、构造函数、程序块)
  • 执行父类的程序块和构造函数
  • 加载子类的非静态成员(属性、方法、构造函数、程序块)
  • 执行子类的程序块和构造函数
  • 将对象空间的地址引用交给变量存储

总结

在 java 中我们用类来描述对象的特征和行为,而对象是通过类创建的。

类由五个成员组成:

  • 属性,用来静态描述特征
  • 方法,用来动态描述行为
  • 构造函数,用来创建对象
  • 程序块,一个特殊的方法,在构造函数执行前执行的
  • this关键字
    • 表示当前类的对象
    • 可以在方法、构造函数、程序块中调用属性、方法
    • 可以作为构造函数,在另一个构造函数中调用,但必须在第一行代码中