类和对象并不是同一种东西,类是对多个对象共同特征(变量)和行为(方法)的抽象,而对象是通过类这个模板实例化出来的存在内存的堆中的数据。

类的成员

类由变量和方法组成,这些都是类的成员,这些成员在内存中的存储位置不一定相同,有的会存储在堆中,这些被称为非静态成员,有的会存储在方法区中,这些被称为静态成员。

静态成员和非静态成员在内存中的区别:
首先静态成员是属于类的,非静态成员是属于对象的。

因此,当我们对一个类进行实例化时,实例化出来的对象中的成员就是该类的所有非静态成员,他们会一起被存储在内存的堆中,也就是实例化了多少个对象就会在堆中存在几份该类的非静态成员。

而类中的静态成员是存储在内存的方法区中(如果静态成员变量的值是对象,那么实际的对象会保存在堆中,变量存放在方法区中),所有对象都可以访问方法区中这一份静态成员的数据,也就是在对象中的非静态成员可以访问静态成员,但静态成员不可以访问非静态成员的数据,因为那时对象可能还没有创建出来,或者同时有多个对象他也分不清具体要访问哪个对象的数据,这些不确定的点可能造成程序运行出错,因此索性直接不允许访问。

下面我将类中的成员按照是否静态进行分类并说明。

非静态成员

非静态成员可以访问静态成员,也就是比如在非静态成员方法内可以获取到静态成员的数据
可通过 类名.静态成员名 或者 this.静态成员名静态成员名 获取,如下

  1. class People {
  2. public static String country;
  3. public String name;
  4. public People(String name) {
  5. this.name = name;
  6. // 在非静态成员方法内可以访问到静态成员变量
  7. // 1.通过 this.静态成员名 的方式获取
  8. this.country = "中国";
  9. // 2.也可以不添加 this,因为编译期会自动添加 this,所以效果是一样的
  10. country = "中国";
  11. // 3.使用 类名.静态成员名 的方式访问
  12. People.country = "中国";
  13. }
  14. }

非静态成员变量

非静态成员变量的声明语法格式和局部变量一样

  1. class 类名 {
  2. 数据类型 成员变量名;
  3. }

成员变量和局部变量也有区别,局部变量需要先初始化值后才能使用,而成员变量一般不需要初始化,但他也有自己的默认值,他的默认值和数组中元素的默认值一样。
非静态成员的特点是,每实例化一个对象时,就需要添加一份非静态成员的数据到该对象中,对于变量来说,也就是,即使这些变量的值是一样的,也需要在每个对象中声明,无法重复使用,因此有个劣势就是当变量的值重复时会浪费内存空间。

非静态成员方法

创建成员方法的语法格式如下

  1. class 类名 {
  2. 返回值类型 成员方法名(形参列表) {
  3. 成员方法体;
  4. }
  5. }
  • 返回值
    把方法体内的数据带到方法体外
  • 形参
    把方法体外的数据带到方法体内
  • 形参列表
    多个形参组成的列表,也就是把多个形参从方法体外带到方法体内
    语法格式: 数据类型 形参变量名1, 数据类型 形参变量名2, ...

构造方法
还有一个特殊的方法是构造方法。
如果一个类的方法的方法名和类名完全相同,那么这个方法就是该类的构造方法,该方法会在用 new 对类进行实例化的时候被自动调用。
他的作用是对成员变量的值进行初始化

创建构造方法的语法格式:

  1. class 类名 {
  2. public 类名(形参列表) {
  3. 构造方法体;
  4. }
  5. }

被调用的场景:

  1. // 用 new 对 Person 类进行实例化时会自动调用 Person 类中的构造方法
  2. Person p = new Person();

注:
1.当类中没有定义任何构造方法时,编译器会自动添加一个无参数空构造方法体的构造方法,该构造方法叫做缺省/默认构造方法,如

  1. class People {
  2. // 编译器自动添加该默认构造方法
  3. public People() {}
  4. }

2.当我们只要提供了构造方法,编译器不会再自动创建任何形式的构造方法

  1. class People {
  2. String name;
  3. // 代码中提供了一个需要传入参数的构造方法
  4. public People(String name) {
  5. this.name = name;
  6. }
  7. public static void main(String[] args) {
  8. // 这里需要不传入参数的构造方法
  9. // 这里会报错,因为类中没有不需要传参的构造方法,编译器也不会在类有构造方法的时候自动创建无参空构的构造方法
  10. People people = new People();
  11. }
  12. }

方法重载(overload)
当多个成员方法的方法名相同而参数列表不同时(不考虑返回值类型),这些方法之间的关系称为重载关系

构成重载关系的形式如下:

  • 参数个数不同 ```java void show() { System.out.println(“show”); }

void show(String s) { System.out.println(s); }

  1. - **参数类型不同**
  2. ```java
  3. void show(int i) {
  4. System.out.println(i);
  5. }
  6. void show(String s) {
  7. System.out.println(s);
  8. }
  • 参数顺序不同 ```java void show(int i, double b) { System.out.println(i + b); }

void show(double b, int i) { System.out.println(b + i); }

  1. **不构成重载关系**的形式如下:
  2. - **参数名不同**
  3. ```java
  4. void show(double a, int b) {
  5. System.out.println(a + b);
  6. }
  7. void show(double b, int i) {
  8. System.out.println(b + i);
  9. }
  • 返回值类型不同 ```java void show(int i, double b) { System.out.println(i + b); }

double show(int i, double b) { System.out.println(i + b); return i + b; }

  1. <a name="dKFQH"></a>
  2. ###
  3. <a name="E2xId"></a>
  4. #### 构造块
  5. 构造块是在类中直接用 {} 括起来的代码块,构造块在每次创建对象时都会执行一次,并且执行顺序在构造方法之前,因此他可以用来在构造方法给成员变量初始化前做些准备工作。
  6. ```java
  7. class People {
  8. // 这是一个构造块
  9. {
  10. // 构造块中的内容会先于构造方法执行
  11. System.out.println("构造块");
  12. }
  13. public People() {
  14. System.out.println("构造方法体");
  15. }
  16. // 该方法运行多次的执行结果都是一致的
  17. public static void main(String[] args) {
  18. People people = new People(); // 控制台打印:构造块 -> 构造方法体
  19. People people2 = new People(); // 控制台打印:构造块 -> 构造方法体
  20. }
  21. }

静态成员

从内存层面来看,静态成员比非静态成员更省空间,因为静态成员是属于类的,只需要单独存储一份在内存的方法区中就行了,每次用类实例化一个对象的时候,静态成员不会被存储在该对象中,也就是该对象所在的堆中,因此节省了空间,也不影响其他对象使用它,因为静态成员是可以被所有对象访问到的。

静态成员变量

声明语法:

  1. class 类名 {
  2. static 数据类型 成员变量名;
  3. }

使用语法:

  1. 引用.静态成员变量名
  2. 类名.静态成员变量名

  1. class People {
  2. // 声明静态成员变量
  3. public static String name;
  4. public People() {
  5. }
  6. public static void main(String[] args) {
  7. People p1 = new People();
  8. System.out.println(p1.name); // null(引用类型默认值是null)
  9. People p2 = new People();
  10. System.out.println(p2.name); // null
  11. // 使用类名.静态成员变量名方式获取(推荐这种)
  12. People.name = "张三";
  13. // 也可以使用引用.静态成员变量名方式获取
  14. // p1.name = "张三";
  15. System.out.println(p1.name); // 张三
  16. System.out.println(p2.name); // 张三
  17. }
  18. }
  19. // 修改静态成员,实际修改的是所有对象都共享的方法区里的变量,因此不管在哪个对象中获取该变量值都是一致的

静态成员方法

静态成员方法内无法访问非静态成员,只能访问静态成员,并且访问时不能在前面加 this. 来引用,因为在静态方法中没有 this 关键字

  1. class People {
  2. public static String country;
  3. public static void test() {
  4. // 这两种是可以的
  5. System.out.println(country);
  6. System.out.println(People.country);
  7. // 这是不行的
  8. System.out.println(this.country);
  9. }
  10. }

静态代码块

使用static 关键字修饰的构造块,先于构造块执行,当加载类的时候就会执行静态代码块,而不是在每次创建对象的时候执行,因此如果类只加载了一次,那么静态代码块最终也只会执行一次,和创建对象无关。
他的作用是随着类的加载做一些准备工作,如加载数据库的驱动包。

  1. class People {
  2. {
  3. // 构造块中的执行顺序先于构造方法
  4. System.out.println("构造块");
  5. }
  6. static {
  7. // 静态代码块执行顺序先于构造块
  8. System.out.println("static");
  9. }
  10. public People() {
  11. System.out.println("构造方法体");
  12. }
  13. public static void main(String[] args) {
  14. People people = new People(); // 控制台打印:static -> 构造块 -> 构造方法体
  15. People people2 = new People(); // 控制台打印:构造块 -> 构造方法体
  16. }
  17. }

this

在类里的非静态成员里可以使用 this 关键字,this 本质上相当于是一个引用了当前对象的引用变量,通过 this 可以获取到当前对象内的数据。
我们在类中获取成员变量和成员方法时有时候可以不用加 this 是因为编译器自动帮我们加了 this。

使用场景:

  • 通过在构造方法的构造方法体内的第一行写 this() 调用其他构造方法

    1. class People {
    2. String name;
    3. People() {
    4. }
    5. People(String name) {
    6. // 通过 this() 调用其他构造方法
    7. this();
    8. this.name = name;
    9. }
    10. public static void main(String[] args) {
    11. People people = new People("s");
    12. }
    13. }
  • 通过 this.xxx 使用该类中的成员变量 & 成员方法

  • 可作为成员方法中的返回值返回,如 return this;

注:
1.如果方法的形参需要赋值给类中的成员变量,推荐将形参名取的和该成员变量名一样,可以提高可读性

  1. class People {
  2. public String name;
  3. // 形参名和对应的成员变量名一样
  4. public People(String name) {
  5. // 用 this. 的方式指明,否则将会赋值给形参自己
  6. this.name = name;
  7. }
  8. }

对象的创建

由于类只是一个抽象的概念,我们无法直接使用,因此我们如果想使用该类,需要用 new 关键字创建该类的对象,也就是将类实例化成一个客观存在的实体(也称实例/对象)。
用 new 创建对象的本质就是在堆区中申请一块存储空间,用来存放该实例的特征信息(行为存储在这里吗?)

创建对象的语法:

  1. new 类名();

上面创建出来的对象是匿名对象,在堆中创建出来后我们无法找到他并使用他,我们可以创建一个引用变量引用这个对象,然后就可以找到这个对象并使用,语法格式如下:

  1. 类名 引用变量名 = new 类名();
  2. /*
  3. 如下
  4. 用 new 关键字创建的对象存在堆区中,赋值运算符左边的引用变量实际在栈中申请了一块存储空间,
  5. 存储的是引用的对象的内存地址,通过这个引用变量,我们可以找到堆区中的这个对象
  6. */
  7. Person person = new Person();

当我们想通过引用变量访问处在堆区中的对象中存储的特征信息时,语法格式如下

  1. 引用变量名.成员变量名;
  2. // 如下
  3. System.out.println(person.name);