Java SE 是什么,包括哪些内容(八)?
本文内容参考自Java8标准
1、为什么需要构造方法
随着计算机革命的发展,"不安全"的编程方式已经逐渐成为编程代价高昂的主因之一!
初始化正是涉及安全的主要问题之一,许多C程序错误都源于程序员忘记初始化变量,所以当你声明一个变量的时候,一定要记得初始化它。
我们再来回顾一下Java在初始化中做的努力:
⑴、基本类型的变量:
①、类变量(有时也被称为成员变量)
声明在方法和代码块以外的变量都是类变量,也就是这个变量是属于一个类的,而不是属于一个方法或一个代码块的。
类变量一旦声明,就能直接使用,因为Java虚拟机会默认初始化!
// 类变量声明后就能直接使用。
//类variable
public class variable{
//声明一个int类型的类变量a,未进行初始化,也就是没有给它赋值;
int a;
//程序执行的入口main方法
public static void main(String[] args){
//创建类对象,不创建类对象无法使用类变量。
variable v = new variable();
//打印a的值,直接使用,程序未报错。结果为0。
System.out.println(v.a);
}
}
实际示例:
如果直接初始化了,那么变量的值就是初始化的值:
另外七种类型的基本变量也会有默认的对应初始化值。
②、局部变量(声明在方法内部或者代码块内部的变量)
// 局部变量不会被默认初始化.
//类variable
public class variable{
//声明一个方法test()
public void test(){
//声明一个变量a,不进行初始化
int a;
//直接打印a的值.
System.out.println(a);
}
//程序执行的入口main方法
public static void main(String[] args){
//创建类对象,不创建类对象无法使用方法。
variable v = new variable();
//调用方法test(),程序直接报错
v.test();
}
}
局部变量一定要进行初始化,它不会被Java默认初始化:
方法:
变量未初始化报错!
代码块(实际上代码块就是一个"{ }",你可以直接在Java代码中直接使用一个"{ }"声明一段代码):
变量未初始化报错!
以上内容是有关基本类型的初始化机制。接下来再看引用类型的初始化机制。
⑵、引用类型的变量:
引用类型就是通过构造方法来确保初始化。
我们假设没有构造方法。
我们假设为每一个类都定义一个名为initialize()的方法(initialize翻译成中文就是初始化的意思),该方法的名称提醒你在使用该类的对象之前都必须调用它,然而,这同时意味着用户必须记得自己去调用此方法,这样使用起来非常的不方便,因为你不能确保每一个人都会记得去调用此方法。
在Java中,通过提供构造器,类的设计者可以确保每一个对象都会得到初始化。创建对象时,如果对应的类具有构造器,Java就会在用户有能力操作对象之前自动调用此构造方法,从而保证了初始化的进行。
接下来的问题就是如何命名这个方法,有两个问题:
①、所取的任何名字都可能与类的另一个方法名称冲突。
②、这个方法是Java的编译器自动调用的,所以你必须让编译器知道方法名称是什么。
Java中采用了C++中的解决方案,简单并且符合逻辑:构造方法的名称与类名称完全相同,这样编译器自然就知道了构造方法,方便自动调用,同时也便于记忆。
构造方法代码举例:
// 构造方法
class Rock{
//这个类的构造方法
Rock(){
//构造方法的内容
System.out.println("Rock");
}
//Java程序执行入口main方法
public static void main(String[] args){
//for循环,循环10次.
for(int i=0;i<10;i++){
//实际上new关键字后面的Rock()就意味着
//调用构造方法,这里表示构造方法被调用了
//10次.结果是打印10个字符串"Rock"
new Rock();
}
}
}
实际代码示例:
从上面示例中你可以很清楚的看到,在创建对象的代码"new Rock()“中,实际上就是调用了构造方法,它将会为对象分配存储空间,这就确保了在你使用对象之前,它已经被恰当的初始化了。
由于构造方法和类名完全相同,所以"每个方法名称首字母小写"的编码风格不适用于构造方法!
实际上,构造方法可以和普通方法一样,按需带参数,不带任何参数的构造方法叫"默认构造器”,Java文档中通常使用术语"无参构造器",接下来,我们来看下构造方法带参数的情况。
构造器带参数通常是为了指定如何创建一个对象。
// 构造方法
class Rock{
//这个类的构造方法,带一个int类型的参数.
Rock(int i){
//构造方法的内容
System.out.println("Rock"+i);
}
//Java程序执行入口main方法
public static void main(String[] args){
//for循环,循环10次.
for(int i=0;i<10;i++){
//实际上new关键字后面的Rock(i)就意味着
//调用构造方法,这里表示构造方法被调用了
//10次.结果是打印10个字符串"Rock"+i值的结果.
new Rock(i);
}
}
}
实际代码示例:
Rock(int i)里面的"int i"是形式参数,在通常的编程过程中简称"形参"。
构造器有了形式参数,就可以在初始化对象时提供实际参数,同时构造器的形式参数可以有多个,每个参数用","隔开既可(比如上面的例子中,形式参数的类型是int,所以在初始化对象的时候,你只能提供int类型的实际参数,如果有多个形式参数,那么提供实际参数的时候一定要按照形式参数的顺序以及类型分别提供)。
例如:假设有一个Tree(树)类型,它有一个构造器,接受整形的变量来表示树的高度,那么,你就能创建一个具有初始高度的Tree(树)对象:
创建一个初始高度为12米的树对象:
Tree t = new Tree(12); t指向的就是内存中一个初始高度为12的Tree(树)对象(很容易与实际联系起来,因为现实中的每一棵树都是有实际的高度的,所以你大概能体会到构造方法的一般用处是什么。)。
构造器有助于减少错误,使代码更易于阅读,从概念上讲,"创建"和"初始化"应该是彼此独立的,然而在构造方法(new Rock(i)/new Tree(12))的形式中,你却找不到对initialize()方法的明确调用,也就是说你找不到明显的初始化的代码(仅仅只是在构造方法中提供实际参数就完成了初始化),在Java中,"初始化"和"创建"捆绑在一起的,两者不能分离(通过new +构造方法(…若干实际参数)就完成了创建和初始化)。
实际上,构造器是一种特殊的方法,因为它是没有返回值的,它与返回值为空(void)的方法有明显的区别,对于空返回值,尽管方法本身不会自动返回什么,但仍可选择让它返回别的东西,构造方法则不会返回任何东西(虽然new +构造方法(…若干实际参数)确实返回了新对象的引用,但是构造方法本身并没有返回任何东西)。
如果构造方法有返回值,并且允许人们自行选择返回类型,那么势必得让编译器知道返回类型,这样非常麻烦,所以干脆没有返回值,因为有了对新对象的引用就行了,如果需要返回什么,通过对象的正常方法返回既可。
2、构造方法总结
⑴、Java中会提供默认的空构造方法:
一定要注意这个"空"字,如果你在代码中未声明任何的构造方法,你只能使用空的构造方法来创建对象。
比如(这里直接上实际的代码示例):
从上面的示例中可以很清楚的看出,Java在你未声明任何的构造方法的情况下会提供一个默认的空构造方法!你可以直接创建对象同时调用方法。
⑵、一旦你声明了构造方法,则Java不会提供任何东西,创建对象的方式以你提供的构造方法为准。
比如(这里直接上实际的代码示例):
从上面的示例可以很清楚的看出,一旦提供了构造方法,则创建对象就以已经提供的构造方法为准了,Java默认的空构造方法就失效了,也就是说,如果提供了构造方法,则Java不再提供任何东西!
⑶、Java中的构造方法在每次创建对象的时候都会执行一次。
这也就意味着:
①、你需要将初始化对象的所有代码都放进构造方法中。
②、你需要将与创建对象有关并且只需要执行一次的代码放进构造方法中。
③、其他你认为有必要放进构造方法中的代码。
举例:
每个人都会有姓名、性别、年龄。创建一个类(Person)代表人类:
// 创建一个类代表人类
public class Person{
//创建一个变量代表姓名
private String name;
//创建一个变量代表性别
private String sex;
//创建一个变量代表年龄
private int age;
//希望创建对象的时候不用指定任何内容,所以需要以下形式的构造方法
public Person(){}
//希望创建对象的时候能指定他的姓名、性别、年龄,所以需要以下形式的构造方法
public Person(String name,String sex,int age){
//赋值操作,将传入的实际参数赋值给当前对象的变量。
//this关键字的作用后面博文会详细提到。
this.name = name;
//同上
this.sex = sex;
//同上
this.age = age;
}
//希望创建对象的时候能指定他的姓名、性别,所以需要以下形式的构造方法
public Person(String name,String sex){
//赋值操作,将传入的实际参数赋值给当前对象的变量。
//this关键字的作用后面博文会详细提到。
this.name = name;
//同上
this.sex = sex;
}
//希望创建对象的时候能指定他的性别、年龄,所以需要以下形式的构造方法
public Person(String name,String sex){
//赋值操作,将传入的实际参数赋值给当前对象的变量。
//this关键字的作用后面博文会详细提到。
this.sex = sex;
//同上
this.age = age;
}
//希望创建对象的时候能指定他的姓名、年龄,所以需要以下形式的构造方法
public Person(String name,String sex){
//赋值操作,将传入的实际参数赋值给当前对象的变量。
//this关键字的作用后面博文会详细提到。
this.name = name;
//同上
this.age = age;
}
//希望创建对象的时候仅指定他的姓名,所以需要以下形式的构造方法
public Person(String name,String sex){
//赋值操作,将传入的实际参数赋值给当前对象的变量。
//this关键字的作用后面博文会详细提到。
this.name = name;
}
//希望创建对象的时候仅指定他的性别,所以需要以下形式的构造方法
public Person(String name,String sex){
//赋值操作,将传入的实际参数赋值给当前对象的变量。
//this关键字的作用后面博文会详细提到。
this.sex = sex;
}
//希望创建对象的时候仅指定他的年龄,所以需要以下形式的构造方法
public Person(String name,String sex){
//赋值操作,将传入的实际参数赋值给当前对象的变量。
//this关键字的作用后面博文会详细提到。
this.age = age;
}
//以上共提供了八种构造方法。你可以选择其中的一种创建对象。
public static void main(String[] args){
//直接创建对象,不提供任何实际的参数
Person p = new Person();
//创建一个姓名是小王,性别男,年龄23岁的对象
//根据构造方法里面的形参顺序依次填入实际参数的值。
Person p1 = new Person("小王","男",23);
//创建一个只知道姓名的对象
Person p2 = new Person("小王");
//剩下的例子可以自己尝试。
//需要注意的是,在创建对象的时候,利用哪个构造方法都行
//最重要的是匹配实际使用参数类型、数量的那个构造方法的形式存在!
}
}
有关构造方法基础的内容就说这么多,如果后续想深入了解,可以继续关注博文!
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明优快云,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正!