Java是面向对象的程序设计语言,类是面向对象的重要内容,可以把类当成一种自定义类型,可以使用类来定义变量,这种类型的变量统称为引用变量。所有类都是引用变量。
面向对象的程序设计过程中有两个重要概念:类(class)和对象(object,也被称为实例,instance),其中类是某一批对象的抽象,可以把类理解成某种概念;对象才是一个具体存在的实体。
[修饰符] class 类名
{
零到多个构造器定义...
零到多个成员变量...
零到多个方法...
对定义类语法格式的详细说明如下:
[修饰符] 类型 成员变量名 [=默认值];
对定义成员变量语法格式的详细说明如下:
[修饰符] 方法返回值类型 方法名(形参列表)
{
//由零条到多条可执行性语句组成的方法体
}
对定义方法语法格式的详细说明如下:
方法体里多条执行语句之间有严格的执行顺序,排在方法体面前的语句总是先执行,排在方法后的语句总是慢执行。
static 是一个特殊的关键字,它可用于修饰方法、成员变量等成员。static修饰的成员表明它属于这个类本身,而不属于该类的单个实例,因为通常把static修饰的成员变量和方法也称为类变量、类方法。不适用static修饰的普通方法、成员变量则属于该类的单个实例,而不属于该类。通常把不使用static修饰的成员变量和方法也称为实例变量、实例方法。
由于static的英文直译就是静态的意思,因此有时也把static修饰的成员变量和方法称为静态变量和静态方法,把不使用static修饰的成员变量和方法称为非静态变量和非静态方法。静态成员不能直接访问非静态成员。
[修饰符] 构造器名(形参列表)
{
// 由零条到多条可执行语句组成的构造器执行体
}
对定义构造器语法格式的详细说明如下:
值得指出,构造器既不能定义返回值类型,也不能使用void声明构造器没有返回值。如果为构造器定义了返回值类型,或使用void声明构造器没有返回值,编译时不会出错,但Java会把这个所谓的构造器当成方法来处理。
在这里写一个Person类:
public class Person
{
//下面定义两个成员变量
public String name;
public int age;
//下面定义了一个say方法
public void say(String content)
{
System.out.println(content);
}
}
创建对象的根本途径是构造器,通过new关键字来调用某个类的构造器即可创建这个类的实例
//使用Person类定义一个Person类型的变量
Person p;
//通过new关键字调用Person类的构造器,返回一个Person实例
//将该Person实例赋给p变量
p = new Person();
上面代码也可简写成如下形式:
//定义p变量的同时并为p变量赋值
Person p = new Person();
static 修饰的方法和成员变量,即可通过类来调用,也可以通过实例来调用;没有使用static修饰的普通方法和成员比变量,只可通过实例来调用。
//访问p的name实例变量,直接为该变量赋值
p.name = "李刚";
//调用p的say()方法,声明say()方法时定义了个形参
p.say("Java语言很简单,学习很容易!");
//直接输出p的name实例变量,将输出 李刚
System.out.println(p.name);
Java提供了一个this关键字,this关键字总是指向调用该方法的对象。根据this出现位置的不同,this作为对象的默认引用的两种情形:
public class Dog
{
//定义一个jump()方法
public void jump()
{
System.out.println("正在执行jump方法");
}
public void run()
{
this.jump();
System.out.println("正在执行run方法");
}
}
对于static修饰的方法而言,则可以使用类来直接调用该方法,如果在static修饰的方法中使用this关键字,则这个关键字就无法指向合适的对象。所以,static修饰的方法中不能使用this引用,由于static修饰的方法不能使用this引用,所以static修饰的方法不能访问不适用static修饰的普通成员:静态成员不能直接访问非静态成员
下面程序演示了静态方法直接访问非静态方法时引发错误。
public class StaticAccessNonStatic
{
public void info()
{
System.out.println("简单的info方法");
}
public static void main(String[] args)
{
// 因为main()方法是静态方法,而info()是非静态方法
// 调用main的方法的是该类本身,而不是该类的实例
//因此省略的this无法指向有效的对象
info();
}
}
会出现下面这样的错误:
无法从静态上下文中引用非静态方法 info()
注意:Java中static修饰的成员属于类本身,而不属于该类的实例,既然static修饰的成员完全不属于该类的实例,那么就不应该允许使用实例去调用static修饰的成员变量和方法!记住:Java编程时不要使用对象去调用static修饰的成员变量、方法,而是应该使用类去调用static修饰的成员变量、方法!
除此以外,this引用也可以用于构造器中作为默认引用,由于构造器是直接使用new关键字来调用,而不是使用对象来调用的,所以在this在构造器中代表该构造器正在初始化的对象。
public class ThisInConstructor
{
//定义一个名为foo的成员变量
public int foo;
public ThisInConstructor()
{
//在构造器里定义一个foo变量
int foo = 0;
/// 使用 this 代表该构造器正在初始化的对象
// 下面的代码将会把该构造器正在初始化的对象的 foo 成员变量6
public static void main(String[] args)
{
//所有使用 ThislnConstructor 创建的对象的 foo 成员变量
// 都将被设为 ,所以下面代码将输出6
System.out.println(new ThisInConstructor().foo);
}
}
如果需要定义方法,则只能在类体内定义,不能独立定义一个方法,如果这个方法使用了static修饰,则这个方法属于这个类,否则这个方法属于这个类的实例。
Java语言里的方法的所属性主要体现在如下方面:
使用 static 修饰的方法属于这个类本身,使用 static 修饰的方法既可以使用类作为调用者来调用,可以使用对象作为调用者来调用。但值得指出的是,因为使用 statlc 修饰的方法还是属于这个类的,因此使用该类的任何对象来调用这个方法时将会得到相同的执行结果,这是由于底层依然是使用这些实例所属的类作为调用者。
&esmp; Java里方法的参数传递方式只有一种:值传递。所谓值传递,就是将实际参数值的副本传入方法内,而参数本身不会受到任何影响。
class DataWrap
{
int a;
int b;
{
public class ReferenceTranferTest
{
public static void swap(DataWrap dw)
{
//下面三行代码实现dw的a、b两个成员变量的值交换
// 定义一个临时变量来保存dw对象的a成员变量的值
int tmp = dw.a;
dw.a = dw.b;
dw.b = tmp;
System.out.println("Swap方法里,a成员变量的值是"+dw.a + ":b成员变量的值是"+dw.b);
}
public static void main(String[] args)
{
DataWrap dw = new DataWrap();
dw.a = 6;
dw.b = 9;
swap(dw);
System.out.println("交换结束后,a成员变量的值是" + dw .a + " ; 成员变量的值是 + dw .b) ;
}
}
程序从main()方法开始执行,main()方法开始创建了一个DataWrap对象,并定义一个dw引用变量来指向DataWrap对象,这是一个与基本类型不同的地方。
Java允许定义形参个数可变的参数,从而允许为方法指定数量不确定的形参。如果在定义方法时,在最后一个形参的类型后增加三点(…),则表明该形参可以接受多个参数值,多个参数值被当成数组传入。
public class Varargs
{
//定义了形参个数可变的方法
public static void test(int a, String... boos)
{
//books 被当数组出路
for(String tmp : books)
{
System.out.peintln(tmp);
}
// 输出整数变量a的值
System.out.println(a);
}
public static void main(String[] args)
{
// 调用test方法
test(5, "哈哈哈","123h");
}
}
Java 允许同一个类里定义多个同名方法,只要形参列表不同就行。如果同一个类中包含了两个或两个以上方法的方法名相同,但形参列表,则被称为方法重载。
从上面介绍可以看出,在Java程序中确定一个方法需要三个要素:
public class Overload
{
//下面定义了两个test()方法
public void test()
{
System.out.println("无参数");
}
public void test(String msg)
{
System.out.println("重载的test方法" + msg);
}
public static void main(String[] args)
{
OverLoad ol = new Overload();
ol.test();
ol.test("heelo");
}
}
变量可以分为两大类:成员变量和局部变量
成员变量指的是在类里定义的变量,也就是前面所介绍的field:局部变量指的是在方法里方法里定义的变量。
成员变量被分为类变量和实例变量两种,定义成员变量时没有static修饰的是就是实例变量,有static修饰的就是类变量。
前面程序中经常出现通过某个对象的直接访问其成员变量的情形,比如将某个Person的age成员变量直接设为1000,显然不合理。
封装(Encapsulation)是面向对象的三大特性之一(另外两个是继承和多态),它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
对一个类进行封装,可以实现下面目的:
Java提供三个访问控制符:private、protected和public,分别代表了3个访问控制级别,另外还有一个不加任何访问控制符的访问控制级别。详细介绍如下:
public class Person
{
//使用private修饰成员变量,将这些成员变量隐藏起来
private String name;
private int age;
//提供方法来操作name成员变量
public void setName(String name)
{
//执行合理性校验,要求用户名必须在2~6之间
if (name.length() > 6 || name.length() < 2}
{
System.out.println("您设置的人名不符合要求");
return;
}
else
{
this.name = name;
}
}
public String getName()
{
return this.name
}
//提供方法来操作age成员变量
public void setAge(int age)
{
//执行合理性校验,要求用户年龄必须在0~100之间
if(age > 100 || age < 0)
{
System.out.println("您设置的年龄不合法");
return;
}
else
{
this.age = age;
}
public int getAge()
{
return this.age
}
}
注意:Java类里实例变量的setter和getter方法有非常重要的意义。例如,某个类里包含了一个名为abc的实例变量,则其对应的setter和getter方法名应为setAbc() 和 getAbc()(将原实例变量名的首字母大写,并在前面分别增加 set get 动坷,就变成 setter,getter方法名)。
关于访问控制符的使用,存在如下几条基本原则
前面提到包范围,先来回忆一个场景:在我们漫长的求学、工作生涯中可曾遇到过与自己同名的同学或同事?因为笔者姓名的缘故,笔者经常会遭遇此类事情。如果同一个班级里出现两个叫“李刚”的同学,那老师怎么处理呢?老师通常会在我们的名字前增加一个限定,例如大李刚、小李刚以示区分。
Java允许将一组功能相关的类放在同一个package下,从而组成逻辑上的类库单元。如果希望把一个类放在指定的包结构下,应该在Java源程序的第一个非注释行放置如下格式的代码:
package packageName;
一旦在Java源文件中使用了这个package语句,就意味着源文件里定义的所有类都属于这个包。位于包中的每个类的完整类名都应该是包名和类名的组合,如果其他人需要使用该包下的类,也应该使用包名加类名的组合。
package lee;
public class Hello
{
public static void main(String[] args)
{
System.out.println("Hello World");
}
}
在该目录下运行:
javac -d . Hello.java
这时候不会单单出现一个 .java文件,还会出现lee文件夹,Hello.java文件会在lee目录中。这时候需要执行文件即
java lee.Hello
上面定义的的位于lee包下的Hello.java及生成的Hello.class文件,建议以下图形式存放:
为了简化编程,Java引入了import关键字,import可以向某个Java文件中导入指定包层次下某个类或全部类,import语句应该出现在package语句之后、类定义之前。一个Java源文件只能包含一个package语句,但是可以有多个import语句。使用import语句导入单个类的用法如下:
import package.subpackage...ClassName;
上面语句用于直接导入指定Java类,使用下面代码:
import lee.sub.Apple;
使用import语句导入指定包下全部类的用法如下:
import package.subpackage...*;
上面import语句中的星号(*)只能代表类,不能代表包。因此使用import lee.*;语句时,它表明导入lee包下的所有类,即Hello类和Hello Test类,而lee包下的sub子包并不导入。
**注意:**Java默认为所有源文件导入java.lang包下的所有类,因此前面在Java程序中使用String、System类时都无须类时都无须使用import语句来导入这些类,但对于前面介绍数组时提到的Arrays类,其位于java.util包下,则必须使用import语句来导入该类。
现在可以总结出Java源文件的大体结构定义:
package 语句 //0个或者1个,必须放在文件开始
import | import static 语句 //0或者多个
public classDefinition | interfaceDefinition | enumDefinition //0个或者一个public类,接口,枚举定义
classDefinition | interfaceDefintion | enumDefinition //0个或多个普通类,接口或枚举定义
构造器是一个特殊的方法,这个特殊的方法用于创建实例时执行初始化。构造器是创建对象的重要途径。
&esmp; 构造器最大的用出就是在创建对象时执行初始化。当创建一个对象时,系统为这个对象的实例变量进行默认初始化,这种默认的初始化把所有基本类型的实例变量设为0或false和null。
public class ConstructorTest
{
public String name;
public int count;
//自定义的构造器,该构造器包含两个参数
public ConstructorTest(String name, int count)
{
this.name = name;
this.count = count;
}
public static void main(String[] args)
{
ConstructorTest tc = new ConstructorTest("ahha",900);
System.out.println(tc.name);
System.out.println(tc.count);
}
}
感觉有点像python中的__init__函数。
同一个类里具有多个构造器,多个构造器的形参列表不同,即被称为构造器重载。构造器允许Java类里包含多个初始化逻辑,从而允许使用不同的构造器来初始化Java对象。
public class ConstructorOverload
{
public String name;
public int count;
//无参数构造器
public ConstructorOverload(){}
//自定义的构造器,该构造器包含两个参数
public ConstructorOverload(String name, int count)
{
this.name = name;
this.count = count;
}
public static void main(String[] args)
{
ConstructorOverload tc01 = new ConstructorOverload();
ConstructorOverload tc02 = new ConstructorOverload("ahha",900);
System.out.println(tc01.name + tc01.count);
System.out.println(tc02.name + tc02.count);
}
}
public class Apple
{
public String name;
public String color;
public double weight;
public Apple(){}
public Apple(String name, String color)
{
this.name = name;
this.color = color;
}
public Apple(String name, String color, double weight)
{
//通过 this 调用另 个重载的构造器的初始化代码
this(name, color);
this.weigth = weight;
}
}
继承是面向对象的三大特性之一,也是实现软件复用的重要手段。Java继承具有单继承的特点,每个子类只有一个直接父类。
Java的继承通过extends来实现。如下:
修饰符 class SubClass extends SuperClass
{
//类定义部分
}
这个比较简单,不写了。
子类扩展了父类,子类是一个特殊的父类。大部分时候。大部分时候,子类总是以父类为基础,额外增加新的成员变量和方法。但是也有需要子类重写父类方法的情况:
public class Bird
{
public void fly()
{
System.out.println("我在飞...");
}
}
public class Ostrich extends Bird
{
//重写Bird类的fly()方法
public void fly()
{
System.out.println("我在跑....");
}
public static void main(String[] args)
{
Ostrich os = new Ostrich();
os.fly();
}
}
这种子类包含父类同名的方法的现象称为方法的重写,也称为方法的覆盖。
方法的重写需要遵守“两同两小一大”规则,“两同”即方法名相同、形参列表相同;“两小”指的是子类方法的返回值类型应该父类方法返回值类型更小或相等,子类方法声明抛出的异常类应该比父类方法声明抛出的异常类更小或相等;“一大”指的是子类方法的访问权限比父类方法权限更大或者相等。
如果需要在子类方法中调用父类被覆盖的实例方法,则可以使用super限定来调用父类被覆盖的实力方法。
public void callOverrideMethod()
{
super.fly();
}
super是Java的一个关键字,super用于限定该对象调用它从父类继承得到的实例变量或方法。正如this不能出现在static修饰的方法中一样,super也不出现在static修饰的方法中。static修饰的方法是属于类的,该方法的调用者可能是一个类,而不是对象。
如果子类里没有包含和父类同名的成员变 ,那么在子类实例方法中访问该成员变 时,则无须显式使用 super 或父类名作为调用者。如果在某个方法中访问名为 的成员变 ,但没有显式指定调用者,则系统查找a的顺序为:
子类不会获得父类的构造器,但子类构造器里可以调用父类构造器的初始 代码,类似于前面所介绍的一个构造器调用另一个重载的构造器。
个构造器中调用另一个重载的构造器使用 this 调用来完成,在子类构造器中调用父类构造器使super 调用来完成。
class Base
{
public double size;
public String name;
public Base(double size, String name)
{
this.size = size;
this.name = name;
}
}
public class Sub extends Base
{
public String color;
public Sub(double size,String name, String color)
{
super(size, name);
this.color = color;
}
public static void main(String[] args)
{
Sub s = new Sub(5.6, "ahda", "dad");
System.out.println(s.size + "--" + s.name + "--" + s.color);
}
}
super调用和this调用也很像,区别在于super调用的是父类的构造器,而this调用的是同一个类中重载的构造器。因此,使用super调用父类构造器也必须出现在子类构造器执行体的第一行,所以this调用和super调用不会同时出现。
class Creature
{
public Creature()
{
System.out.println("Creature无参数的构造器");
}
}
class Animal extends Creature{
public Animal(String name)
{
System.out.println("Aniaml自带一个参数的构造器:" +name);
}
public Animal(String name, int age)
{
this(name);
System.out.println("Animal自带两个参数的构造器:" + name + age);
}
}
public class Wolf extends Animal
{
public Wolf()
{
super("狼", 3);
System.out.println("Wolf无参数的构造器");
}
public static void main(String[] args)
{
new Wolf();
}
}
运行结果:
Creature无参数的构造器
Aniaml自带一个参数的构造器:狼
Animal自带两个参数的构造器:狼3
Wolf无参数的构造器
看得出来,构造器都是从最原始往下开始,然后如果有this调用同类的构造器,则依次执行。
Java引用变量有两个类型:一个是编译时类型,一个运行时类型。编译时类型由声明该变量时使类型决定,运行时类型由实际赋给变量的对象决定。如果编译时类型时运行时类型不一致,就可能出现所谓的多态。
class BaseClass
{
public int book = 6;
public void base()
{
System.out.println("父类的普通方法");
}
public void test()
{
System.out.println("父类的被覆盖的方法");
}
}
public class SubClass extends BaseClass
{
public String book = "hahah";
public void test()
{
System.out.println("子类的覆盖父类的方法");
}
public void sub()
{
System.out.println("子类的普通方法");
}
public static void main(String[] args)
{
BaseClass bc = new BaseClass();
//输出6
System.out.println(bc.book);
//下面两次调用将执行BaseClass的方法
bc.base();
bc.test();
//下面编译时类型和运行时类型完全一样,因此不存在多态
SubClass sc = new SubClass();
System.out.println(sc.book);
sc.base();
sc.test();
//下面编译时类型和运行时类型不一样,多态发生
BaseClass ploymophicBc = new SubClass();
System.out.println(ploymophicBc.book);
ploymophicBc.base();
ploymophicBc.test();
}
}
当把一个子类对象直接赋给父类引用变量时,例如上面的BaseClass ploymophicBc = new SubClass(); 这个ploymophicBc引用变量的编译时类型为BaseClass,而运行时类型时SubClass,当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。
上面的main()方法中如果使用ploymophicBc.sub();这行代码会在编译时引发错误。虽然ploymophicBc引用变量实际上确实包含sub()方法,但编译类型为BaseClass,因此无法调用sub()方法。
与方法不同的是,对象的实例变量则不具备多态性,比如上面的ploymophicBc引用变量,程序中输出它的book实例变量时,并不是输出SubClass类里定义的实例变量,而是输出BaseClass类的实例变量。
引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时执行它运行时类型具有的方法,因此,编写Java代码的时候,引用变量只能调用声明该变量时所用类包含的方法。通过引用变量来访问其包含的实例变量时,系统总是试图访问它编译时类型所定义的成员变量,而不是它运行时类型所定义的成员变量。
编写Java程序时,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,即使它实际所引用的对象确实包含该方法。如果需要让这个引用变量调用它运行时类型的方法,则必须把它强制类型转换成运行时类型,强制类型转换需要借助于类型转换运算符。
类型转换运算符是小括号,类型转换运算符的用法是:(type)variable,这种用法可以将variable变量转换成一个type类型的变量。这种强制类型转换需要注意:
public class ConversionTest
{
public static void main(String[] args)
{
double d = 13.4;
long l = (long)d;
System.out.println(l);
int in = 5;
//试图把一个数值类型的变量转换为boolen类型,
//编译时会提示:不可转换类型
//boolean b = (boolean)in;
Object obj = "Hello";
//obj变量的编译时类型为Object与String存在继承关系,可以强制类型转换
//而且obj变量的实际类型是String
String objStr = (String)obj;
System.out.println(objStr);
//定义一个objPri变量,编译时类型为Object,实际类型为Integer
Object objPri = Integer.valueOf(5);
String str = (String)objPri;
}
}
考虑到进行强制类型转换时可能出现异常,因此进行类型转换之前应先通过。instanceof运算符来判断是否可以成功转换。例如,上面的String str = (String)objPri; 代码运行时会引发 ClassCastException异常,这是因为 objPri 不可转换成 String 类型 为了让程序更加健壮,可以将代码改为如下:
if (objPri instanceof String)
{
String str = (String)Objpri;
}
在进行强制类型转换之前,先用 instanceof 运算符判断是否可以成功转换,从而避免出现 ClassCastException 异常,这样可以保证程序更加健壮。
继承是实现类复用的重要手段,但继承带来了一个最大的坏处:破坏封装。组合也是实现类复用的重要方式,而采用组合方式来实现复用则能提供更好的封装性。
继承带来了高度复用的同时,也带来了严重的问题:继承严重地破坏了父类地封装性。前面介绍封装时提到:每个类都应该封装它内部信息和实现细节,而只暴露必要的方法给其他类使用。但在继承关系中,子类可以直接访问父类的成员变量和方法,从而创造子类和父类严重耦合。
为了保证父类具有良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则:
组合是把旧类对象作为新类的成员变量组合进来,以实现新类的功能。通常需要在新类中使用private修饰被组合的旧类对象。
class Animal
{
private void beat()
{
System.out.println("心脏跳动...");
}
public void breath()
{
beat();
System.out.println("吸一口气,吐一口气,呼吸中");
}
}
class Bird
{
// 将原来的父类组合到原来的子类,作为子类的一个组合成分
private Animal a;
public Bird(Animal a)
{
this.a = a;
}
//重新定义一个自己breath()方法
public void breath()
{
//直接复用Animal提供breath()方法来实现Bird的breath()方法
a.breath();
}
public void fly()
{
System.out.println("我在天空自由飞翔");
}
class Wolf()
{
// 将原来的父类组合到原来的子类,作为子类的一个组合成分
private Animal a;
public Wolf(Animal a)
{
this.a = a;
}
public void breath()
{
//直接复用Animal提供breath()方法来实现Bird的breath()方法
a.breath();
}
public void run()
{
System.out.println("我在陆地上快速奔跑");
}
}
public class CompositeTest
{
public static void main(String[] args)
{
Animal a1 = new Animal();
Bird b = new Bird(a1);
b.breath();
b.fly();
Animal a2 = new Animal();
Wolf w = new Wolf(a2);
w.breath();
w.fly();
总之,继承要表达的是一种“是(is-a)”的关系,而组合表达的是“有(has-a)”的关系。
Java使用构造器来对单个对象进行初始化操作,使用构造器完成整个Java对象的状态初始化,然后将Java对象返回给程序,从而让该Java对象的信息更加完整。
初始化块是 Java 类里可出现的第 种成员(前面依次有成员变量、方法和构造器), 一个类里可以有多个初始化块,相同类型的初始化块之间有顺序 前面定义的初始化块先执行,后面定义的初始化块后执行。初始化块的语法格式如下:
[修饰符]{
// 初始化块可执行代码
...
}
初始化修饰符只能是static,使用static修饰的初始块被称为静态初始化块。
public class Person
{
{
int a = 6;
if (a > 4){
System.out.println("Person初始化模块a>4");
}
System.out.println("Person第一个初始化模块a>4");
}
{
System.out.println("Person第二个初始化块");
}
public Person
{
System.out.println("Person类的无参数构造器");
}
pubilc static void main(String[] args)
{
new Person();
}
}
输出:
Perso口初始化块:局部变量 的值大于
Person 的初始化块
Person 的第 个初始化块
Person 类的无参数构造器
从运行结果可以看出,当创建 Java 对象时,系统总是先调用该类里定义的初始化块,如果一个类里定义了2个普通初始化块,则前面定义的初始化块先执行,后面定义的初始化块后执行.
从某种程度上来看,初始化块是构造器的补充,初始化块总是在构造器执行之前执行。系统同样可使用初始化块来进行对象的初始化操作。
与构造器不同的是,初始化块是一段固定执行的代码,它不能接收任何参数 因此初始化块对同一个类的所有对象所进行的初始化处理完全相同 。基于这个原因,不难发现初始化块的基本用法,如果有段初始化处理代码对所有对象完全相同,且无须接收任何参数 ,就可以把这段初始化处理代码提取到初始化块中 显示了把两个构造器中的代码提取成初始化块示意图。