一.构造函数
1.构造函数的作用:给对应的对象进行初始化。
2.构造函数的定义格式:
修饰符 函数名(形式参数){
函数体;
}
3.构造函数要注意的细节:
(1)构造函数是没有返回值类型的。
(2)构造函数的函数名必须要与类名一致。
(3)构造函数并不是由我们手动调用的(手动调用指的是如b1.baby();),而是在创建对应的对象时,JVM就会主动调用到对应的构造函数。
(4)如果一个类没有显式的写上一个构造方法时,那么Java编译器会为该类添加一个无参的构造函数的。
(5)如果一个类已经显式的写上一个构造方法时,那么Java编译器则不会再为该类添加一个无参的构造方法。
(6)构造函数是可以在一个类中以函数重载的形式存在多个的。
4.构造函数与普通函数的区别:
(1)返回值类型的区别:
①构造函数是没有返回值类型的,如果加上void或者别的返回值类型,就变成一个普通的函数,就需要我们 手动去调用。
②普通函数是有返回值类型的,即使函数没有返回值,返回值类型也要写上void。
(2)函数名的区别:
①构造函数的函数名必须要与类名一致。
②普通函数的函数名只要符合标识符的命名规则即可。
(3)调用方式的区别:
①构造函数是在创建对象的时候由JVM调用的。
②普通函数是由我们使用对象调用的,一个对象可以调用多次普通的函数。
(4)作用上的区别:
①构造函数的作用用于初始化一个对象。每创建一个对象就会有一个初始值。
②普通函数是用于描述一类事物的公共行为的。
5.注意事项:
(1)Java编译器添加的无参的构造方法的权限修饰符与类的权限修饰符是一致的。
(2)构造函数是创建对象的时候调用的,所以就可以把属性值传到构造函数中,在构造函数中设置参数接收属性值。
(3)JVM和Java编译器是不同的,Java编译器编译生成的.class文件是给JVM看的,所以经过编译后的class类打开后会是乱码,我们可以通过反编译来查看。
6.补充:
(1)jdk提供了一个java开发工具(javap.exe)给我们进行反编译。
(2)javap反编译工具的使用格式:
javap -c -l -private 类名
7.实例一:
(1)需求:使用Java类描述现实中的婴儿,现实生活中有两种婴儿,一种婴儿一出生就具备了名字(白户),还有一种婴儿出生之后才有名字(黑户)。
(2)实例:
class Baby{
int id; //身份证
String name;//名字
//带参的构造函数
public Baby(int i,String n){
id = i;
name = n;
System.out.println("Baby的属性初始化完毕!");
}
//无参的构造函数
public Baby(){
System.out.println("无参的构造函数被调用了");
}
//哭的行为
public void cry(){
System.out.println(name+"哇哇的哭……");
}
}
class Demo1{
public static void main(String[] args){
//创建一个baby对象
Baby b1 = new Baby(110,"张三");//白户,白户指的是这句话一执行完毕就让婴儿有名字
System.out.println("编号:"+b1.id+"姓名:"+b1.name);
b1.cry();
Baby b2 = new Baby();//黑户
b2.id = 112;
b2.name = "李四";
System.out.println("编号:"+b2.id+"姓名:"+b2.name);
b2.cry();
}
}
(3)运行结果:
8.实例二:
(1)需求:描述一个员工类,员工具备的属性有编号,姓名,年龄,员工具备的公共行为是工作。要求一旦创建一个员工对象的时候,那么该员工对象就要有对应的属性值。
(2)实例:
class Employee{
int id; //编号
String name; //姓名
int age; //年龄
//构造函数
public Employee(int a,String b,int c){
id = a;
name = b;
age = c;
}
public void work(){
System.out.println(name+"工作中……");
}
}
class Demo2{
public static void main(String[] args){
//创建一个员工对象
Employee e = new Employee(110,"狗娃",20);
System.out.println("编号:"+e.id+" 名字:"+e.name+" 年龄:"+e.age);
e.work();
}
}
(3)运行结果:
二.构造代码块
1.构造代码块的作用与构造函数的作用的对比:
(1)构造代码块的作用:给所有对象进行统一的初始化,对象一建立就运行并且优先于构造函数,比如所有的婴儿出生都会哭。
(2)构造函数的作用:给对应的对象进行初始化。
2.构造代码块的格式:
{
构造代码块;
}
注意:构造代码块的大括号必须位于成员的位置上。
3.代码块的类别:
(1)构造代码块:在工作中经常会用到。
(2)局部代码块:大括号位于方法之内,基本上写不写没什么区别,现实开发中也很少会用到。它的作用是缩短局部变量的生命周期,节省一点点内存。
(3)静态代码块:使用static修饰的代码块。
4.实例一:
(1)需求:创建婴儿类,有黑户和白户两种,但是要求不管黑户还是白户只要婴儿一出生就要哭。如果需要每个小孩出生都哭的话,那么就需要在不同的构造函数中都调用cry( )函数,但是这样会造成代码重复问题,应该怎么解决?
(2)分析:可以使用构造代码块。
(3)实例:
class Baby{
int id; //身份证
String name; //名字
//构造代码块……
{
cry();
}
//带参的构造函数
public Baby(int i,String n){
id = i;
name = n;
}
//无参的构造函数
public Baby(){
}
public void cry(){
System.out.println("哇哇地哭……");
}
}
class Demo3{
public static void main(String[] args){
Baby b1 = new Baby(110,"张三");
System.out.println("编号:"+b1.id+" 名字:"+b1.name);
new Baby(112,"李四");
}
}
(4)运行结果:
5.构造代码块要注意的事项:
(1)Java编译器在编译一个Java源文件的时候,会把成员变量的声明语句提前至一个类的最前端。
(2)成员变量的初始化工作其实都是在构造函数中执行的。
(3)一旦经过Java编译器编译后,那么构造代码块的代码就会被移动到构造函数中执行,构造代码块的代码是在构造函数之前执行的,构造函数中的代码是最后执行的。
(4)成员变量的显示初始化与构造代码块的代码是按照当前代码的顺序执行的。
6.实例二
(1)实例2.1
class Demo1{
int i = 100;
//构造代码块
{
i = 300;
}
//构造函数
public Demo1(){
i = 200;
}
public static void main(String[] args){
Demo1 d = new Demo1();
System.out.println("i="+d.i);//200
}
}
运行结果如下图所示:
(2)实例2.2
class Demo2{
int i = 100;
//构造函数
public Demo2(){
i = 200;
}
//构造代码块
{
i = 300;
}
public static void main(String[] args){
Demo2 d = new Demo2();
System.out.println("i="+d.i);//200
}
}
运行结果如下图所示:
(3)实例2.3
class Demo3{
//构造函数
public Demo3(){
i = 200;
}
//构造代码块
{
i = 300;
}
int i = 100;
public static void main(String[] args){
Demo3 d = new Demo3();
System.out.println("i="+d.i);//200
}
}
运行结果如下图所示:
Demo3反编译后的运行结果如下图所示:
综合以上三个实例可知,构造函数的位置不管放在哪里,都是最后一个赋值的。把Demo3反编译后发现:虽然我们把声明变量的语句放在了最后(变量应该是先声明后使用),但是却没有报错,是因为Java编译器对代码进行了调整,Java编译器把声明变量的语句调整到了最前面;虽然我们写的代码一声明就赋值,但是经过Java编译器调整后的代码声明变量后并没有马上赋值,成员变量的初始化工作都是在构造函数中执行,Java编译器会把这些代码移动到构造函数中。当赋值操作移动到构造函数中时,构造代码块的代码是优先于构造函数中的代码执行的。
7.实例三
(1)实例3.1
class Demo4{
//构造代码块
{
i = 300;
}
int i = 100;
public static void main(String[] args){
Demo4 d = new Demo4();
System.out.println("i="+d.i);//100
}
}
运行结果如下图所示:
(2)实例3.2
class Demo5{
int i = 100;
//构造代码块
{
i = 300;
}
public static void main(String[] args){
Demo5 d = new Demo5();
System.out.println("i="+d.i);//300
}
}
运行结果如下图所示:
由以上两个实例可知:
成员变量的显示初始化与构造代码块的代码是按照当前代码的顺序执行的。