8.构造方法和静态
本章目标
- 构造方法
- 属性初始化顺序
- 对象数组
- 类的静态成员
本章内容
对象的属性挨个赋值太麻烦,能不能创建对象时直接赋值?
我们在创建对象时使用new Person(),那么这个Person()是什么意思呢?
贯穿案例中客户的积分,每次创建一个新的用户时默认积分应该是一致的,这该怎么处理?
一、构造方法
java创建对象的三个步骤:申请内存、调用构造方法、返回对象引用
1、什么是构造方法
构造函数是类的一种特殊方法,每次创建类的实例都会调用它,用来初始化对象
对象的创建顺序:
- 在使用对象之前,必须要根据类的定义构造并初始化一个对象(指定其属性的值)
- 在构造对象时,通过
new
运算符调用类的构造方法 - 用来将对象的各个属性初始化为指定的状态
- 对象一旦构造完成,就不能再一次使用构造方法来重新设置属性的值
2、特征
- 构造方法的方法名与类名一样
- 构造方法可以带多个参数,也可以没有参数
- 构造方法没有返回值
- 构造方法总是通过
new
运算符 - 使用构造方法可以重载
- 程序中如果没有为类提供任何构造方法,编译器将自动提供一个默认的构造方法(没有参数)
- 不能对已经存在的对象调用构造方法
3、语法及使用
在带有参数的构造函数中,类在实例化时必须传递参数,否则该构造函数不能被执行
[访问修饰符] <类名> (参数列表){
// 构造函数的主体
}
3.1、 默认构造方法
每个类都有一个默认不带参的构造方法,无论是否显示声明
在直接创建对象时都是调用默认不带参的构造方法
public Employee() {
// TODO Auto-generated constructor stub
}
3.2、声明带参构造方法
当显示声明了带参构造方法之后 ,默认的构造方法将失效,除非我们显示的声明
public Employee(int id) {
this.id = id;
}
3.3、构造方法重载
当我们显示的声明了默认构造方法之后 ,此时类中有两个构造方法,也就实现了构造方法重载
创建对象时,调用不同构造方法,给对象初始化状态不同
public Employee() {
// TODO Auto-generated constructor stub
}
public Employee(int id) {
this.id = id;
}
3.4、定义全参构造方法
public class Employee {
public Employee() {
// TODO Auto-generated constructor stub
}
public Employee(int id) {
this.id = id;
}
public Employee(int id, String name, String sex, int age, double height) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.height = height;
}
……
}
3.5、测试调用
public class Test {
public static void main(String[] args) {
Employee Employee = new Employee();
Employee Employee2 = new Employee(1001);
Employee Employee3 = new Employee(1002,"姚明","男",43,226);
System.out.println(Employee.getId());
System.out.println(Employee2.getId());
System.out.println(Employee3.getId());
}
}
3.6、运行结果
0
1001
1002
4、this的使用
this关键字在类定义中提供了当前对象的成员或构造方法的途径,this仅限于在构造函数
、类
的方法
和类的实例
中使用。
-
用来访问当前对象的数据成员,使用形式如下:
this.数据成员
-
用来访问当前对象的成员方法,使用形式如下:
this.成员方法(参数)
-
当有重载的构造方法时,用来引用同类的其他构造方法,其使用形式如下:
this(参数)
注:在访问成员属性或成员方法时可以省略,如果方法中传入参数和类属性同名,一定需在类字段前加上this
5、成员变量与局部变量
5.1、概念
- 成员变量:直接定义在类中。
- 局部变量:定义在方法、构造函数或块内以及方法的参数。
5.2、作用域
- 成员变量:作用范围是整个类,可以在类的所有方法中访问;
- 局部变量:域仅限于定义它的方法,不能被其它方法调用;
5.3、初始值
- 成员变量:每个成员变量一个默认初始值;
- 局部变量:局部变量没有初始值,在使用之前必须手动赋值;
5.4、成员变量和局部变量同名时
如果局部变量和成员变量同时,局部变量的赋值具有更高的优先级,优先取局部变量的值;
5.5、示例
public class Demo {
int globalVar;//成员变量/全局变量/字段/属性
public void test() {
int localVar = 11;
System.out.println(localVar);
}
public void test2() {
int globalVar = 11;//同名时,局部优先
//System.out.println(localVar);无法调用
System.out.println(globalVar);
}
}
二、属性初始化顺序(理解)
Java语言里,new表达式总体负责两个动作:
- 分配对象空间并对其做默认初始化。默认初始化会将对象的所有成员字段设到其类型对应的默认值(零值)。
- 初始化对象
- 其中构造器只负责第2点,第1点是包含在new表达式里的语义
1、属性默认初始值
JVM为每一个类的每一个构造方法都创建一个
()方法,用于初始化实例变量, ()方法是虚拟机自己调用的,不在我们的程序中显性表示,
如果对象在实例化时,其属性(状态)既没有通过构造方法进行初始化,也没有通过声明属性时赋值进行显式初始化,将会自动地赋予默认值:
- 数值类型使用 0;
- 布尔类型使用 false;
- 引用类型使用 null;
注意:局部变量不会被自动赋值,必须被明确地初始化
-
给Employee类加个flag字段
private boolean flag; public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; }
-
测试类中输出
public class Test { public static void main(String[] args) { Employee Employee = new Employee(); System.out.println(“数字类型默认值:”+Employee.getId()); System.out.println(“引用类型默认值:”+Employee.getName()); System.out.println(“布尔类型默认值:”+Employee.isFlag()); } }
-
结果
数字类型默认值:0 引用类型默认值:null 布尔类型默认值:false
2、声明赋值
对象实例化时,其属性(状态)初始化的方法有很多,除了使用构造方法进行初始化以外,还可以在声明属性时通过简单地赋值进行初始化,以设置一个基本的内容
private String address = "地址不详";
private int point = 0;
3、初始化块
初始化块是java类的一个成员,与属性、方法、构造器属于平等地位
它的主要作用是初始化类属性和对象属性
初始化块是用花括号括起来的一段代码块
{
this.address = "未知地址";
}
4、对象初始化顺序
在对象实例化的过程中,有多种方法对对象的各个属性进行初始化。通常Java在构造对象时按照以下顺序进行操作:
- 所有属性被初始化为默认值;
- 按照声明初始化属性;
- 执行初始化块;
- 构造方法
- 嵌套调用构造函数(暂不接触);
示例
public class Employee {
private String address = "地址不详";
{
System.out.println("初始化块之前用户地址:"+this.address);
this.address = "未知地址";
}
public Employee(int id, String address) {
System.out.println("构造方法之前用户地址:"+this.address);
this.id = id;
this.address = address;
}
……
}
调用
public class Test {
public static void main(String[] args) {
Employee Employee = new Employee(1001,"科技一路");
System.out.println("用户最终地址="+Employee.getAddress());
}
}
运行结果
初始化块之前用户地址:地址不详
构造方法之前用户地址:未知地址
用户最终地址=科技一路
三、对象数组
1、什么是对象数组
在对象数组中每一个元素都是指向某个对象的引用(对象属于同一个类型)。
例如:定义一个”学生”类,班级里每一名学生都是这个类型的一个实例,都有各自的姓名、性别、年龄和考试成绩
Student[] stu = new Student[10];
stu[0] = new Student("张三丰", "male", 20 ,77);
2、案例 (贯穿项目相关)
用Employee[]数组来保存5员工的信息.
挑出其中的未成年人(不满18岁),输出他们的姓名和年龄?
public class Test {
public static void main(String[] args) {
Employee[] employees = new Employee[10];
employees[0] = new Employee(1001, "姚明", "男", 43, 7226);
employees[1] = new Employee(1002, "张三", "男", 18, 8178);
employees[2] = new Employee(1003, "李四", "男", 16, 7168);
employees[3] = new Employee(1004, "王五", "男", 19, 9180);
employees[4] = new Employee(1005, "男七", "男", 17, 8170);
for (Employee employee : employees) {
if (employee != null && employee.getAge() < 18) {
System.out.println(employee.getName() + ":" + employee.getAge());
}
}
}
}
3、案例 (贯穿项目相关)
输出对象时看不到对象详细信息?这时怎么办
重写Employee类的toString()方法
-
重写toString()
@Override public String toString() { return "Employee [address=" + address + ", id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", salary=" + salary + ", flag=" + flag + "]"; }
-
输出对象测试
public class Test {
public static void main(String[] args) { Employee[] employees = new Employee[10]; employees[0] = new Employee(1001, "姚明", "男", 43, 7226); employees[1] = new Employee(1002, "张三", "男", 18, 8178); employees[2] = new Employee(1003, "李四", "男", 16, 7168); employees[3] = new Employee(1004, "王五", "男", 19, 9180); employees[4] = new Employee(1005, "男七", "男", 17, 8170); for (Employee employee : employees) { if (employee != null && employee.getAge() < 18) { System.out.println(employee); } } }
-
结果
Employee [address=未知地址, id=1003, name=李四, sex=男, age=16, salary=8168.0, flag=false] Employee [address=未知地址, id=1005, name=男七, sex=男, age=17, salary=9170.0, flag=false]
四、类的静态成员
思考 :
1、上周我们一直在使用static修饰方法,而面向对象之后很少再使用static,那么什么时候该使用static定义方法?
2、Employee类中添加一个count字段,来统计一共创建了多少个用户,这个该使用什么字段来修饰?
1、static关键字
静态分配:类加载时分配内存
用static修饰符修饰,可以是变量、方法或代码块
- 类中的静态变量、方法或代码块属于类,而不属于某个特定的对象。
- 类的静态成员可以与类的名称一起使用,而无需创建类的对象。
- 静态变量或方法也称为类的变量或方法
2、静态属性
用static修饰符修饰的数据成员属于类的静态数据成员
2.1、静态属性特点:
- 不管创建了类的多少实例,整个类中静态变量的副本只有一个。
- static类数据成员仍属于类的作用域,还可以使用public static、 private static等进行修饰。修饰符不同,可访问的层次也不同。
2.2、引用静态变量的方法:
- 在类的内部可以直接使用静态变量
- 从类的外部可以通过类名访问静态的公共变量
- 从类的外部可以通过类的任一实例访问静态的公共变量(不推荐使用)
2.3、示例
public static int count = 0;
public Employee(int id, String name, String sex, int age, double salary) {
System.out.println("欢迎您"+name+"你是第"+(++count)+"个用户");
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.salary = salary;
}
2.4、测试
public class Test {
public static void main(String[] args) {
Employee[] employees = new Employee[10];
employees[0] = new Employee(1001, "姚明", "男", 43, 7226);
employees[1] = new Employee(1002, "张三", "男", 18, 8178);
employees[2] = new Employee(1003, "李四", "男", 16, 7168);
employees[3] = new Employee(1004, "王五", "男", 19, 9180);
employees[4] = new Employee(1005, "男七", "男", 17, 8170);
for (Employee employee : employees) {
if (employee != null && employee.getAge() < 18) {
System.out.println(employee);
}
}
}
}
2.5、结果
欢迎您姚明你是第1个用户
欢迎您张三你是第2个用户
欢迎您李四你是第3个用户
欢迎您王五你是第4个用户
欢迎您男七你是第5个用户
一共有5个用户
3、静态方法
3.1、与静态方法相关的几个要点:
- 类的静态方法只能访问其他的静态成员
- 静态方法没有this
- 静态方法不能被覆盖
- 可以通过类名调用静态方法(也可以使用对象调用,但不推荐);
3.2、示例
非静态方法中既可以调用实例方法和属性,又可以调用静态方法属性
public class StaticDemo {
int x;
static int y;
void foo() {
x = 1; // 正确,等价于this.x = 1
y = 1; // 正确,等价于Test.y = 1
}
static void goo() {
// x = 1; // 错误不能访问 this.x
y = 1; // 正确,等价于Test.y = 1
}
}
3.3、测试
在类外部,静态成员通过类来调用
public class TestStatic {
public static void main(String[] args) {
StaticDemo.goo();
//StaticDemo.foo(); 报错
StaticDemo staticDemo = new StaticDemo();
staticDemo.foo();
}
}
4、静态初始化块(了解)
如果需要通过计算来初始化静态变量,可以声明一个静态块
-
静态块仅在该类被加载时执行一次
-
只能初始化类的静态数据成员
-
静态初始化器不是方法,没有方法名、返回值和参数列表
static int[] values = new int[10]; // 静态数组成员 static { for (int i = 0; i < values.length; i++) { values[i] = (int) (100.0 * Math.random()); } }
5、几种成员赋值时机(了解)
使用 javap -v Test.class 命令查看其字节码:
静态变量
(类变量)在类加载过程的初始化阶段才会被赋值常量
在编译的时候就会被分配具体值:成员变量
在对象初始化时赋值(这里指的是调用()方法时,不是构造方法)
思维导图
本文来自博客园,作者:icui4cu,转载请注明原文链接:https://www.cnblogs.com/icui4cu/p/18802524