Java面向对象基础:类与对象、构造方法、this、static、代码块及包机制。

面向对象概念

面向过程和面向对象

面向过程:主要注重“先做什么后做什么”。核心思想是先分析解决问题的步骤,然后用代码、方法把这些步骤一步一步的实现。代表性语言是C语言。

优点:性能比面向对象的语言高,适合开发单片机

缺点:只适合开发一些小型的、简单的产品,不易维护、不易扩展、不易复用

面向对象:主要注重宏观上的把控。更符合人们的思考方式,将复杂的问题简单化,将复杂的问题拆分成一个一个的小问题,这些小问题的处理上又是先干什么、后干什么。宏观上使用面向对象把控,微观上依然使用面向过程处理。

优点:易维护、易扩展、易复用。开发出的产品高内聚、低耦合。适合做一些大型的商业级应用,比如商城APP、社交软件

缺点:性能相较于面向过程偏低。

举例

如何盖房子

面向过程

- 设计房子
- 购买材料
- 雇佣工人
- 修建毛坯房
- 装修
- 入住

面向对象

- 设计师:设计房子
- 采购员:负责购买材料
- 包工头:负责雇佣工人
- 建筑工人:负责盖房子
- 装修工人:负责房屋装修
- 业主:负责入住

类和对象

类:类就是一类事物的抽象,抽象就是提取这一类事物共有的属性和行为。

比如学生都拥有姓名、年龄、学号等属性,有吃饭、睡觉、学习等行为。这些就可以抽象为一个学生类。

对象(实例):万物皆对象。生活中能够接触到的任何一个具体的事物都是一个对象。

我们可以将类看做成一个模板或者图纸,用户根据这个模板去填充某个具体事物的一些属性,从而创造出一个特指的事物,这个事物就是对象。

面向对象基础

类的创建

使用关键字 class 来定义一个类,一个java文件中可以同时定义多个class,但是public修饰的class只能有一个

1
2
3
修饰符 class 类名 {

}

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
package unit1;

public class Demo1Object {
}

class Person {

}

class Dog {

}

类的组成

  • 属性 field:修饰符 数据类型 属性名 = 默认值;
  • 方法(行为) method:修饰符 返回值类型 方法名(参数列表) {}

事实上,这里称之为“属性”是不准确的,严格上来说我们应该称之为成员变量或静态变量,方法包括成员方法、静态方法

成员方法不需要加static,而静态方法则需要加static

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package unit1;

public class Student {
String name;
int age;

void eat(String food) {
System.out.println(name + "正在吃" + food);
}

void study() {
System.out.println(name + "今年" + age + "岁,正在学习Java");
}

}

创建对象

对象(object)也叫作实例(instance)。要创建一个对象,就需要有一个类,使用 new 关键字来创建一个对象

1
类名 对象名 = new 类名();

可以使用 对象.成员变量 来访问这个对象的属性,可以使用 对象.成员方法名 来调用成员方法

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package unit1;

public class Demo2Student {
public static void main(String[] args) {
// 创建一个Student对象
Student stu1 = new Student();
stu1.name = "古力娜扎";
stu1.age = 23;
System.out.println("name:"+stu1.name+ " age:" + stu1.age);
stu1.eat("啤酒小龙虾");
stu1.study();
System.out.println("=============");
Student stu2 = new Student();
stu2.name = "迪丽热巴";
stu2.age = 24;
System.out.println("name:"+stu2.name+ " age:" + stu2.age);
stu2.study();
stu2.eat("冰激凌");
}
}

注意:成员变量隶属于对象,同一个类多个对象之间的成员变量值互不影响

成员变量默认值

在类中定义成员变量时可以不进行赋值(不进行显式初始化),JVM默认会对其进行隐式初始化

数据类型 默认值
byte、short、int、long 0
float、double 0.0
char \u0000
boolean false
引用类型 null
1
2
3
4
public static void main(String[] args) {
Student stu = new Student();
System.out.println("name:"+stu.name+ " age:" + stu.age);
}

对象内存分析

01-对象内存分析

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package unit1;

public class Demo3ObjectMemory {
public static void main(String[] args) {
Student stu = new Student();
stu.name="迪丽热巴";
stu.age = 23;
addAge(stu);
System.out.println(stu.age);
System.out.println("======");
int num = 23;
addNum(num);
System.out.println(num);
System.out.println("======");
int[] arr = {23};
addArr(arr);
System.out.println(arr[0]);
}

public static void addArr(int[] arr) {
arr[0]++;
}

public static void addNum(int num) {
num++;
}

public static void addAge(Student stu) {
stu.age++;
}

}

随堂练习

1、 使用计算器(Calculator)完成加法和减法运算,并能显示出该计算器的品牌和尺寸。

计算器成员变量:品牌(brand)、尺寸(size)

计算器成方法:加法运算(add)、减法运算(sub)和显示计算机信息(show)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package unit1;

public class Demo4Calculator {
public static void main(String[] args) {
Calculator calculator = new Calculator();
calculator.brand = "联想";
calculator.size = 5;
int add = calculator.add(1, 2, 3, 4);
System.out.println("加法运算:" + add);
int sub = calculator.sub(100, 1, 2, 3, 4);
System.out.println("减法运算:" + sub);
calculator.show();
}
}

成员变量与局部变量的区别

局部变量是定义在方法或者代码块中的变量,成员变量则是定义在类中的变量,和成员方法处在同一层。

区别

  1. 定义位置不同
    1. 成员变量定义在类中,整个类中的所有成员方法都可以访问
    2. 局部变量定义在方法或代码块中,只有所属的区域有效
  2. 在内存中的位置不同
    1. 局部变量存在于栈内存的方法中
    2. 成员变量存在于堆内存的对象中
  3. 生命周期不同
    1. 成员变量随着对象的创建而存在,随着对象的销毁而销毁
    2. 局部变量随着所属区域的执行而存在,随着所属区域的执行完毕而销毁
  4. 初始化不同
    1. 成员变量有默认初始化值
    2. 局部变量没有默认初始化值
  5. 可用修饰符不同
    1. 成员变量可以被public、protected、private、static、final等修饰符修饰
    2. 局部变量只能被final修饰

当成员变量和局部变量名称冲突时,使用就近原则。先找局部变量,有就使用,没有就找成员变量。

匿名对象

通过使用new关键字来创建对象,一般还会创建一个引用用来接收这个对象,但有的时候也可以不去创建这个引用,此时创建出来的对象叫做匿名对象。

当我们只是想调用一次对象的成员方法时,可以使用匿名对象。一般情况下使用匿名对象是不去使用它的成员变量的,因为这样没有什么意义。

当一个方法中需要的参数是一个对象时,也可以使用匿名对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package unit1;

public class Demo5Object {
public static void main(String[] args) {
// Student student = new Student();
// student.study();
new Student().name = "张三";
new Student().age = 23;
new Student().study();
method(new Student());
}
public static void method(Student student) {
student.study();
}
}

构造方法

构造方法简介

构造方法也叫作构造器,用于给对象进行初始化操作

语法

1
2
3
修饰符 类名(参数列表) {
代码块
}
  1. 构造方法的方法名必须与类名完全一致
  2. 构造方法不能定义返回值类型,方法体中不允许return具体的值
  3. 构造方法必须使用new关键字调用,是一种特殊的方法
1
2
3
public Student() {
System.out.println("构造方法被执行了");
}

问题:对象的创建完全是由构造方法实现的吗?

不是。构造方法是创建java对象的其中一步,对象的创建有以下几步

  1. 分配对象存储空间
  2. 将成员变量进行默认初始化
  3. 执行成员变量的显式初始化
  4. 执行构造方法
  5. 返回对象的地址,使用遍历接收

无参构造方法

如果类中没有定义构造方法,那么会默认提供一个无参构造方法

image-20230423201012453

有参构造方法

有参构造方法一般用来给成员变量进行赋初值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package constructor;

public class Person {
String name;
int age;
String gender;
public Person() {
System.out.println("无参构造方法执行了");
}

public Person(String name, int age, String gender) {
System.out.println("有参构造方法执行了");
// 如果局部变量和成员变量同名,可以使用this来区分成员变量
this.name = name;
this.age = age;
this.gender = gender;
}


}

  1. 如果在类中定义了有参构造,默认的无参构造就没有了
  2. 当局部变量和成员变量同名时,使用this关键字来区分成员变量
  3. 养成习惯,不要省略无参构造

构造方法重载

构造方法也可以进行重载,当new对象时,JVM会自动根据传参的数据类型、顺序、个数来匹配符合的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
package constructor;

public class Demo3Constructor {
public static void main(String[] args) {
Person p1 = new Person("迪丽热巴");
p1.show();
Person p2 = new Person("古力娜扎", 23);
p2.show();
Person p3 = new Person("马尔扎哈", 24, "男");
p3.show();
}
}

构造方法和成员方法的区别

  1. 定义语法区别
    1. 构造方法的方法名要与类名保持一致,并且不能定义返回值类型
    2. 成员方法方法名只需要符合标识符命名规范即可,必须定义返回值类型
  2. 调用时期区别
    1. 构造方法在对象创建时调用
    2. 成员方法在对象创建成功后调用
  3. 调用方式区别
    1. 构造方法用new关键字来调用
    2. 成员方法用对象来调用
  4. 调用次数区别(对于同一个对象)
    1. 成员方法在对象创建成功后,可以调用任意次数
    2. 构造方法只能在创建对象时调用一次

随堂练习

1、 定义一个坐标类(Point),用于表示二维空间中的一个坐标位置。通过坐标类的方法,实现计算两个坐标位置之间的距离(要求:使用构造方法)。

坐标类成员变量:X轴点(x)、Y轴点(y)

坐标类构造方法:Point(double x, double y)

坐标类成员方法:计算两个坐标位置之间的距离(v)

point类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package constructor;

public class Point {

double x;
double y;

public Point(double x, double y) {
this.x = x;
this.y = y;
}

/**
* 计算当前坐标与传入坐标之间的距离
* @param point
* @return
*/
public double calcLength(Point point) {
// this就代表当前对象,谁来调用这个方法,this就代表谁
double xLength = this.x - point.x;
double yLength = this.y - point.y;
// Math.sqrt方法用于开平方
return Math.sqrt(xLength * xLength + yLength * yLength);
}

}

测试类

1
2
3
4
5
6
7
8
9
10
11
package constructor;

public class Demo4Point {
public static void main(String[] args) {
Point p1 = new Point(4, 5);
Point p2 = new Point(1, 9);
double length = p1.calcLength(p2);
System.out.println(length);
}
}

Java中数据类型传递

Java中只有值传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package constructor;

public class Demo5Trans {
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
System.out.println("交换前:num1 = " + num1 + ",num2 = " + num2);
swap(num1, num2);
System.out.println("交换后:num1 = " + num1 + ",num2 = " + num2);
System.out.println("========================");
Person p1 = new Person("迪丽热巴", 23);
Person p2 = new Person("古力娜扎", 24);
System.out.println("交换前:p1.age = " + p1.age + ",p2.age = " + p2.age);
swap(p1, p2);
System.out.println("交换后:p1.age = " + p1.age + ",p2.age = " + p2.age);
change(p1);
System.out.println("+1后:p1.age = " + p1.age);
}

public static void swap(int[] arr1, int[] arr2) {
int[] tmp = arr1;
arr1 = arr2;
arr2 = tmp;
}

public static void swap(int[] arr, int i1, int i2) {
int tmp = arr[i1];
arr[i1] = arr[i2];
arr[i2] = tmp;
}

public static void change(Person p) {
p.age ++;
}

public static void swap(Person p1, Person p2) {
Person tmp = p1;
p1 = p2;
p2 = tmp;
}

public static void swap(int n1, int n2) {
int tmp = n1;
n1 = n2;
n2 = tmp;
}


}

交换、修改成功与否,取决于操作的是堆内存还是栈内存,这就证明了java中只有值传递

随堂练习

  1. 猜数字游戏:一个类 A 有一个成员变量 v,随机生成0-100之间的整数。 定义一个类,对 A 类的成员变量 v 进行猜。 如果大了则提示大了,小了则提示小了。 等于则提示猜测成功

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    package constructor.test;

    import java.util.Scanner;

    /**
    * 猜数字游戏:一个类 A 有一个成员变量 v,随机生成0-100之间的整数。
    * 定义一个类,对 A 类的成员变量 v 进行猜。
    * 如果大了则提示大了,小了则提示小了。 等于则提示猜测成功
    */
    public class Test1 {
    public static void main(String[] args) {
    A a = new A();
    Scanner sc = new Scanner(System.in);
    while (true) {
    System.out.println("请猜一个数字:");
    int num = sc.nextInt();
    if(num > a.v) {
    System.out.println("猜大了,请继续猜");
    }else if(num < a.v) {
    System.out.println("猜小了,请继续猜");
    }else {
    System.out.println("猜中了");
    break;
    }
    }
    }
    }

    class A {
    int v;

    public A() {
    int num = (int) (Math.random() * 101);
    this.v = num;
    }
    }
  2. 请定义一个交通工具(Vehicle)的类其中有: 属性: 速度(speed)、 体积(size)等,方法:移动(move())、设置速度(setSpeed(int speed))、加速 speedUp()、减速 speedDown()等。最后在测试类 Vehicle 中的 main()中实例化一个交通工具对象并通过方法给它初始化 speed,size 的值并且通过打印出来。另外调用加速、减速的方法对速度进行改变。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    package constructor.test;

    /**
    * 2. 请定义一个交通工具(Vehicle)的类其中有:
    * 属性: 速度(speed)、 体积(size)等,
    * 方法:移动(move())、设置速度(setSpeed(int speed))、
    * 加速 speedUp()、减速 speedDown()等。
    * 最后在测试类 Vehicle 中的 main()中实例化一个交通工具对象
    * 并通过方法给它初始化 speed,size 的值并且通过打印出来。
    * 另外调用加速、减速的方法对速度进行改变。
    */
    public class Test2 {
    public static void main(String[] args) {
    Vehicle vehicle = new Vehicle();
    vehicle.size = 20;
    vehicle.setSpeed(80);
    vehicle.move();
    vehicle.speedUp();
    vehicle.move();
    vehicle.speedDown();
    vehicle.move();
    }
    }

    class Vehicle {
    int speed;
    double size;

    public Vehicle() {
    }

    public Vehicle(int speed, double size) {
    this.speed = speed;
    this.size = size;
    }

    public void move() {
    System.out.println(size + "的车正在以每小时" + speed + "的速度行驶");
    }

    public void setSpeed(int speed) {
    this.speed = speed;
    System.out.println("速度设置成功,当前车速为:" + this.speed);
    }

    public void speedUp() {
    this.speed++;
    }
    public void speedDown() {
    this.speed--;
    }

    }
  3. 定义一个圆类——Circle,在类的内部提供一个属性:半径(r),同时 提供 两个 方 法 : 计算 面积 ( getArea() ) 和 计算 周长(getPerimeter()) 。 通过两个方法计算圆的周长和面积并且对计算结果进行输出。最后定义一个测试类对 Circle 类进行使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    package constructor.test;

    /**
    * 定义一个圆类——Circle,在类的内部提供一个属性:半径(r),
    * 同时 提供 两个 方 法 : 计算 面积 ( getArea() )
    * 和 计算 周长(getPerimeter()) 。
    * 通过两个方法计算圆的周长和面积并且对计算结果进行输出。
    * 最后定义一个测试类对 Circle 类进行使用。
    */
    public class Test3 {
    public static void main(String[] args) {
    Circle circle = new Circle(3);
    double area = circle.getArea();
    double perimeter = circle.getPerimeter();
    System.out.println("半径为3的圆,面积为:" + area + ",周长为:" + perimeter);
    }
    }

    class Circle {
    double r;

    public Circle(double r) {
    this.r = r;
    }

    public double getArea() {
    final double PI = 3.14;
    return PI * r * r;
    }

    public double getPerimeter() {
    final double PI = 3.14;
    return PI * r * 2;
    }
    }
  4. 定义一个网络用户类,要处理的信息有用户 ID、用户密码、 email 地址。在建立类的实例时把以上三个信息都作为构造函数的参数输入, 其中用户 ID 和用户密码是必填,email地址是用户 ID 加上字符串”@gameschool.com”

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package constructor.test;

    /**
    * 定义一个网络用户类,要处理的信息有用户 ID、用户密码、 email 地址。
    * 在建立类的实例时把以上三个信息都作为构造函数的参数输入,
    * 其中用户 ID 和用户密码是必填,
    * 默认email地址是用户 ID 加上字符串"@gameschool.com"
    */
    public class Test4 {
    public static void main(String[] args) {
    NetUser u1 = new NetUser("1", "admin", "123@qq.com");
    u1.show();
    NetUser u2 = new NetUser("2", "123456", null);
    u2.show();

    }
    }

    class NetUser {
    String id;
    String password;
    String email;

    public NetUser(String id, String password, String email) {
    this.id = id;
    this.password = password;
    if(email == null) {
    this.email = this.id + "@gameschool.com";
    }else {
    this.email = email;
    }
    }

    public void show() {
    System.out.println("用户id:" + this.id +
    "用户密码:" + this.password +
    "用户email:" + this.email);
    }

    }

this关键字

在创建一个对象成功后,虚拟机还会动态的分配一个引用,这个引用就指向当前对象,就是this关键字

this可以调用本类中的成员变量、成员方法,还可以在构造方法中调用其他的构造方法。

谁来调用,this就指谁。

调用成员变量

当局部变量和成员变量同名时,成员变量会被屏蔽,此时要访问成员变量就要使用 this.成员变量名 形式来进行调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package thisstudy;

public class Person {
String name;
int age;

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

public void show() {
// 在局部变量和成员变量不同名时,可以省略this,此时编译期会为我们加上
System.out.println("name:" + this.name + ", age:" + age);
}
}

调用成员方法

访问成员方法时可以使用 this.成员方法 来进行调用,也可以省略this,编译器会为我们加上。

1
2
3
4
5
6
7
8
9
public void eat() {
System.out.println(name + "正在吃饭");
}

public void show() {
// 在局部变量和成员变量不同名时,可以省略this,此时编译期会为我们加上
System.out.println("name:" + this.name + ", age:" + age);
this.eat();
}

构造方法之间的调用

在一个类的构造方法内部,可以使用this关键字调用其他的构造方法,这样就可以降低代码的重复。

注意:

  1. this只能用在构造方法中来调用构造方法,且必须是第一行语句
  2. this在编译时根据参数列表来选择调用对应的构造方法
  3. this不能递归调用,防止死循环。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Person() {
this(null, 18);
System.out.println("无参构造被调用了");
}

public Person(String name, int age) {
System.out.println("有参构造被调用了");
this.name = name;
System.out.println("校验age是否合法");
if(age < 18) {
System.out.println("age不合法,当前未成年");
System.out.println("即将把age初始化为18岁");
age = 18;
}
this.age = age;
System.out.println("age赋值成功");
}

随堂练习

  1. 定义一个类 Calculaion, 其中包含四个方法: 加(add()) 、 减(sub()) 、乘(times()) 和除(div()) 。 创建一个具有 main()函数的类。 在 main()函数中创建一个 Calculation 的实例对象并对其中的方法进行调用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package thisstudy;

    public class Calculation {

    String brand;
    int size;

    public Calculation(String brand, int size) {
    this.brand = brand;
    this.size = size;
    }

    public int add(int num1, int num2) {
    this.show();
    return num1 + num2;
    }
    public int sub(int num1, int num2) {
    this.show();
    return num1 - num2;
    }
    public int times(int num1, int num2) {
    this.show();
    return num1 * num2;
    }
    public int div(int num1, int num2) {
    this.show();
    return num1 / num2;
    }
    public void show() {
    System.out.println("欢迎使用"+this.brand+"计算器");
    }

    }

  2. 创建一个空调,并调用制冷、制热、通风功能。空调包含的属性有品牌、匹数、温度,功能有加热、制冷、通风等功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package thisstudy;

    public class AirCondition {

    String brand;
    int power;
    double temperature;

    public AirCondition(String brand) {
    this.brand = brand;
    }

    public AirCondition(String brand, int power) {
    this(brand);
    this.power = power;
    }

    public AirCondition(String brand, int power, double temperature) {
    this(brand, power);
    this.temperature = temperature;
    }

    public void cold() {
    System.out.println("制冷功能已开启");
    }
    public void hot() {
    System.out.println("制热功能已开启");
    }
    public void wind() {
    System.out.println("通风功能已开启");
    }
    public void show() {
    System.out.println("品牌:" + this.brand +
    ",匹数:" + this.power +
    ",温度:" + this.temperature);
    }
    }

static 关键字

static修饰的被称为静态的,static关键字可以用来修饰变量和方法、代码块。

静态变量

在类中,与成员变量平级,被static关键字修饰的变量我们称之为 静态变量或类变量

静态变量的特点

  1. 静态变量优先于对象存在的,当类被加载完毕后静态变量就存在了。
  2. 一个类中,静态变量只有一份,可以被该类和该类的所有对象所共享
  3. 我们可以使用 类名.静态变量 或者 对象.静态变量 来进行调用,建议使用前者。

Student类

1
2
3
4
5
6
7
8
9
10
11
12
13
package staticstudy;

public class Student {
String name;
int age;
static String room;

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

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package staticstudy;

public class Demo1Static {
public static void main(String[] args) {
Student s1 = new Student("张三", 23);
s1.room = "一班";
Student s2 = new Student("李四", 24);
// s1和s2调用的room其实是同一个变量,因此不建议使用对象去调用静态变量
System.out.println("s2.name:" + s2.name + ",s2.age:" + s2.age + ",s2.room:" + s2.room);
System.out.println("s1.name:" + s1.name + ",s1.age:" + s1.age + ",s1.room:" + s1.room);
System.out.println(Student.room);

}
}

图解静态变量

02-静态变量内存分析

静态变量和成员变量区别

  1. 生命周期不同
    1. 成员变量随着对象的创建而创建,随着对象的销毁而销毁
    2. 静态变量随着类的第一次加载而存在,随着类的消失而消失
  2. 调用方式不同
    1. 成员变量使用对象.变量名调用
    2. 静态变量一般使用类名.变量名调用
  3. 数据存储位置不同
    1. 成员变量存储在堆内存中,属于对象
    2. 静态变量存在于方法区中,属于类
  4. 创建次数不同
    1. 成员变量每次创建一个对象都会创建出一份
    2. 静态变量只会创建一次,因为类只会被加载一次

静态方法

static修饰的方法也称之为 静态方法 或者 类方法

特点

  1. 静态方法优先于对象存在,随着类的加载就已经存在了
  2. 可以使用 类名.方法名() 进行调用,也可以使用 对象.方法名() 进行调用

Student类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package staticstudy;

public class Student {
String name;
int age;
static String room;

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

public static void study() {
System.out.println("好好学习、天天向上");
}

}

调用

1
2
3
4
5
6
7
8
package staticstudy;

public class Demo2Static {
public static void main(String[] args) {
Student.study();
}
}

引申代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package staticstudy;

public class Student {
String name;
int age;
static String room;

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

public void show() {
// 成员方法中可以调用成员变量和静态变量
System.out.println("name:" + this.name + ",age:" + this.age);
System.out.println("room:" + Student.room);
// 成员方法中可以调用成员方法和静态方法
this.method1();
Student.study();
}

public void method1() {
System.out.println("成员方法被调用了");
}

public static void study() {
System.out.println("好好学习、天天向上");
// 静态方法中只能调用静态变量,不能调用成员变量
// System.out.println("name:" + this.name + ",age:" + this.age);
System.out.println("room:" + Student.room);
// 静态方法中可以调用静态方法,不能调用成员方法
Student.staticMethod();
// show();
}

public static void staticMethod() {
System.out.println("静态方法被调用了");
}

}

静态方法和成员方法区别

  1. 访问变量区别
    1. 成员方法可以访问成员变量和静态变量
    2. 静态方法只能访问静态变量
  2. 访问方法区别
    1. 成员方法可以访问成员方法和静态方法
    2. 静态方法只能访问静态方法
  3. 访问this
    1. 成员方法中可以访问this
    2. 静态方法中不能访问this

简单记忆:static的只能调用static

代码块

广义上:代码块就是存在于类、方法中,使用大括号包裹起来的一段代码

狭义上:一般只认为类中用大括号括起来的属于代码块

静态代码块

在类中,与静态变量、静态方法平级的,使用static修饰的代码块叫做静态代码块,主要用于给类做初始化操作。

1
2
3
4
5
6
7
8
9
10
public class Student {
String name;
int age;
static String room;

static {
System.out.println("静态代码块执行了");
room = "一班";
}
}

特点

  1. 静态代码块只能定义在类中,不能定义在方法中
  2. 一个类中允许有多个静态代码块,执行时按照定义的顺序执行
  3. 静态代码块中的遍历都是局部变量,访问只能访问类的静态变量,不能访问成员变量
  4. 静态代码块随着类的加载而自动执行,并且只会执行一次

静态代码块可以用于给静态变量赋初值,或者用于执行一些全局的初始化操作

构造代码块

在类中,与静态代码块平级,没有static修饰的代码块叫做构造代码块,主要用于给对象进行一些初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Student {
String name;
int age;
static String room;

static {
System.out.println("静态代码块1执行了");
room = "一班";
}

{
System.out.println("构造代码块1执行了");
System.out.println(Student.room);
System.out.println(name);
}
}

特点

  1. 构造代码块只能定义在类中,不能定义在方法中
  2. 一个类可以定义多个构造代码块,执行时按照定义的顺序执行
  3. 构造代码块中的变量都是局部变量,既可以访问静态变量也可以访问成员变量
  4. 构造代码块随着对象的创建而执行,每创建一个对象就执行一次

我们使用构造代码块一般用于给对象进行一些初始化操作

代码块执行顺序

Person类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package staticstudy;

public class Person {
static {
System.out.println("静态代码块1执行了");
}

public Person() {
System.out.println("空参构造执行了");
}

{
System.out.println("构造代码块1执行了");
}
static {
System.out.println("静态代码块2执行了");
}
public Person(int num) {
this();
System.out.println("有参构造执行了");
}
{
System.out.println("构造代码块2执行了");
}
}

测试类

1
2
3
4
5
6
7
8
9
10
package staticstudy;

public class Demo4Statiuc {
public static void main(String[] args) {
Person p1 = new Person(1);
System.out.println("===============");
Person p2 = new Person(1);
}
}

结论:代码执行顺序按照 静态代码块->构造代码块->构造方法 的顺序进行执行

package包

package概述

为什么使用包?

包的作用是为我们管理java类,给类提供了多层命名空间。开发中,我们会遇到大量同名的类,通过包我们可以很容易解决类重名问题,也可以有效地对类进行管理。

包的命名规则

一般情况下,包名使用公司域名反着写,再加上模块名,比如 baidu.com

包名命名遵循标识符规则,一般只采用全部小写字母,多层包之间使用 . 进行连接。

包名不能以 java 开头

如何使用包

定义在指定包下的类,第一行必须要使用 package 关键字声明全包名

1
2
3
4
5
package cn.pdd.product;

public class ProductInfo {
}

类的访问

使用一个类时,需要进行导包操作。

1
import 包名.类名;

导包有两个特殊场景

  1. 当当前类与待使用类存在同一个包下时,可以不进行导包
  2. 当待使用类是 java.lang 包下的,可以不进行导包

包在使用时还需要注意一些细节

  1. 当一个类中导入了两个同名的类时,其中一个必须使用 全类名 来展示,即 包名.类名