Java笔记 —— 初始化

h2{
color: #4ABCDE;
}
a{
text-decoration: none !important;
}
a:hover{
color: red !important;
text-decoration: underline !important;
}
pre{
border: solid 1px #CCCCCC;
border-radius: 3px;
background-color: #F8F8F8;
margin: 15px;
overflow: auto;
white-space: pre;
font-size: 13px;
font-family: consolas, courier, monospace;
line-height: 20px;
padding: 6px 10px;
tab-size: 4;
}
p.textRight{
text-align: right;
}
p.header{
color: #787878;
font-size: 20px;
font-family: 楷体, "微软雅黑", arial;
font-weight: bold;
}
span.bold{
font-weight: bold;
}

一、属性初始化和构造器调用

1)编译器会为属性指定一个默认的初始值,例如:int i; 默认初始值为 0,  double d; 默认初始值为0.0

2)局部变量使用前必须由程序员指定初始值

3)在构造器被调用之前,属性就被初始化了

例1:


package com.example;

public class Values{
private boolean bt;
private char c;
private byte b;
private short s;
private int i;
private float f;
private double d;
private Values v; public void show(){
System.out.println("属性的默认初始化值:");
System.out.println("boolean: " + bt);
System.out.println("char: " + c);
System.out.println("byte: " + b);
System.out.println("short: " + s);
System.out.println("int: " + i);
System.out.println("float: " + f);
System.out.println("double: " + d);
System.out.println("Values: " + v);
}
}

package com.example;

public class Test{
public static void main(String[] args){
Values val = new Values();
val.show();
}
}

运行结果为:

属性的默认初始化值:
boolean: false
char:
byte: 0
short: 0
int: 0
float: 0.0
double: 0.0
Values: null
  • 编译器为所有的属性都指定了一个默认初始值
  • 编译器为对象的引用指定的默认初始值是 null,例如:例子中的 private Values v;
  • 编译器为 char 类型指定的初始值是0(ASCII 中的 NUL),是空字符,这里以空白表示

例2:


package com.example;

public class Man{
public void speak(){
int i;
System.out.println("i = " + i); // error,i 是局部变量,局部变量使用之前必须要初始化
}
}

例3:


package com.example;

public class Man{
public Man(int i){
System.out.println("Man" + "(" + i + ")");
}
}

package com.example;

public class Woman{
public Man m1 = new Man(1);
public Man m2 = new Man(2); public Woman(){
System.out.println("Woman()");
} public Man m3 = new Man(3);
}

package com.example;

public class Test{
public static void main(String[] args){
new Woman();
}
}

运行结果为:

Man(1)
Man(2)
Man(3)
Woman()
  • 这个例子不是使用系统的默认值对属性进行初始化,而是通过我指定的值对属性进行初始化
  • Woman 类中有三个属性 m1,m2,m3 和一个 构造器 Woman(),从运行结果可以看出:属性先被初始化,然后构造器才被调用
  • 属性先被初始化这一特性与属性的位置无关,例如:Woman 类中 m3 就位于构造器的后面,但 m3 也在构造器被调用之前就已经被初始化了

二、初始化的顺序

1)属性分为静态属性和非静态属性,且静态属性比非静态属性先初始化,即初始化顺序为:静态属性 -> 非静态属性 -> 构造器调用

2)静态属性只会在创建对象或者被直接调用的时候初始化(在主类中静态属性也会被初始化),非静态属性只会在创建对象的时候初始化

3)无论创建多少个对象,静态属性只占用一块存储空间,所以静态属性只会初始化一次,而非静态属性在每次创建对象时都会初始化

4)含有继承关系的初始化:当创建一个子类对象时,该对象会包含一个父类的对象,那么保证父类对象初始化的方法是:在子类构造器中调用父类构造器

例1:


package com.example;

public class Pear{
public Pear(int i){
System.out.println("Pear" + "(" + i + ")");
}
}

package com.example;

public class Apple{
public Pear p1 = new Pear(1);
public static Pear p2 = new Pear(2); public Apple(){
System.out.println("Apple()");
} public static Pear p3 = new Pear(3);
}

package com.example;

public class Test{
public static void main(String[] args){
new Apple();
}
}

运行结果:

Pear(2)
Pear(3)
Pear(1)
Apple()
  • 该例子中用 new 创建了 Apple 对象,所以 Apple 类中的静态属性和非静态属性都会初始化
  • 初始化顺序:静态属性 -> 非静态属性 -> 构造器调用,例如:例子中先输出 Pear(2) 和 Pear(3),然后输出 Pear(1),最后调用 Apple() 构造器,所以是先初始化静态属性 p2 和 p3,然后初始化非静态属性 p1,最后调用构造器 Apple()

例2:


package com.example;

public class Pear{
public Pear(int i){
System.out.println("Pear" + "(" + i + ")");
}
}

package com.example;

public class Apple{
public static String color = "red";
public static int number = 10;
public String size = "big";
}

package com.example;

public class Test{
public static void main(String[] args){
System.out.println("Hello World !");
System.out.println("color: " + Apple.color);
}
static Pear p1 = new Pear(1);
static Pear p2 = new Pear(2);
Pear p3 = new Pear(3);
}

运行结果:

Pear(1)
Pear(2)
Hello World !
color: red
  • Test 类是主类,所以静态属性 p1 和 p2 都会初始化。非静态属性 p3 不会初始化,是因为我们没有创建 Test 对象
  • Apple 类中有两个静态属性和一个非静态属性,该例子中调用了 Apple.color ,静态属性 color 在调用的时候会初始化为 red,而静态属性 number 和非静态属性 size 没有被调用,也没有创建对象,所以此时它们不会初始化

例3:


package com.example;

public class Cat{
public Cat(int i){
System.out.println("Cat" + "(" + i + ")");
}
}

package com.example;

public class Dog{
public Cat c1 = new Cat(1);
public static Cat c2 = new Cat(2); public Dog(){
System.out.println("Dog()");
} public static Cat c3 = new Cat(3);
}

package com.example;

public class Test{
public static void main(String[] args){
System.out.println("第一次创建对象");
Dog d1 = new Dog();
System.out.println("第二次创建对象");
Dog d2 = new Dog();
}
}

运行结果:

第一次创建对象
Cat(2)
Cat(3)
Cat(1)
Dog()
第二次创建对象
Cat(1)
Dog()
  • 第一次创建对象的时候,静态属性和非静态属性都初始化了,但是第二次创建对象的时候,只有非静态属性会再次初始化,静态属性只初始化一次

例4:

父类不含构造器


package com.example;

public class T{
public T(int i){
System.out.println("T" + "(" + i + ")");
}
}

package com.example;

public class Person{ // 父类
public static T t4 = new T(4);
public T t5 = new T(5);
public static T t6 = new T(6);
}

package com.example;

public class Man extends Person{ // 子类
public static T t1 = new T(1);
public T t2 = new T(2);
public static T t3 = new T(3); public Man(){
System.out.println("Man()");
}
}

package com.example;

public class Test{
public static void main(String[] args){
new Man();
}
}

运行结果:

T(4)
T(6)
T(1)
T(3)
T(5)
T(2)
Man()
  • 某个类不含构造器时,编译器会为这个类生成一个默认构造器,该生成的默认构造器是一个无参构造器。这些操作是在编译时完成的,所以我们看不见
  • 当父类含有默认构造器时,编译器会自动在子类的构造器中调用父类的构造器来对父类进行初始化。这个过程也是在编译时完成的,所以我们看不见
  • 初始化的顺序:父类静态属性 -> 子类静态属性 -> 父类非静态属性 -> 子类非静态属性 -> 调用子类构造器

父类含有无参构造器


package com.example;

public class T{
public T(int i){
System.out.println("T" + "(" + i + ")");
}
}

package com.example;

public class Person{ // 父类
public static T t4 = new T(4);
public T t5 = new T(5);
public static T t6 = new T(6); public Person(){ // 无参构造器
System.out.println("Person()");
}
}

package com.example;

public class Man extends Person{ // 子类
public static T t1 = new T(1);
public T t2 = new T(2);
public static T t3 = new T(3); public Man(){
System.out.println("Man()");
}
}

package com.example;

public class Test{
public static void main(String[] args){
new Man();
}
}

运行结果:

T(4)
T(6)
T(1)
T(3)
T(5)
Person()
T(2)
Man()
  • 父类含有无参构造器时和父类含有默认构造器时差不多,编译器会自动在子类的构造器中调用父类的构造器来对父类进行初始化
  • 不同的是,当我们创建了构造器之后,编译器就不会再为类创建一个默认构造器了
  • 初始化顺序:父类静态属性 -> 子类静态属性 -> 父类非静态属性 -> 父类构造器调用 -> 子类非静态属性 -> 子类构造器调用

父类只含有有参构造器


package com.example;

public class T{
public T(int i){
System.out.println("T" + "(" + i + ")");
}
}

package com.example;

public class Person{ // 父类
public static T t4 = new T(4);
public T t5 = new T(5);
public static T t6 = new T(6); public Person(int i){
System.out.println("Person" + "(" + i + ")"); // 有参构造器
}
}

package com.example;

public class Man extends Person{ // 子类
public static T t1 = new T(1);
public T t2 = new T(2);
public static T t3 = new T(3); public Man(){
super(1); // super 关键字表示调用父类构造器,且必须位于子类构造器的第一行,每个子类的构造器只能调用父类构造器一次
System.out.println("Man()");
}
}

package com.example;

public class Test{
public static void main(String[] args){
new Man();
}
}

运行结果:

T(4)
T(6)
T(1)
T(3)
T(5)
Person(1)
T(2)
Man()
  • 当父类只有有参构造器时,子类构造器中必须使用 super 关键字调用父类构造器,否则会报错。
  • 当子类有多个构造器时,子类中的每个构造器都必须要调用父类构造器(如果父类有多个构造器,那么子类构造器只需挑父类构造器的其中一个来调用即可,不过一定要保证子类的每个构造器都有调用父类构造器)

父类既含有无参构造器,也含有有参构造器


package com.example;

public class T{
public T(int i){
System.out.println("T" + "(" + i + ")");
}
}

package com.example;

public class Person{ // 父类
public static T t4 = new T(4);
public T t5 = new T(5);
public static T t6 = new T(6); public Person(){ // 无参构造器
System.out.println("Person()");
}
public Person(int i){
System.out.println("Person" + "(" + i + ")"); // 有参构造器
}
}

package com.example;

public class Man extends Person{ // 子类
public static T t1 = new T(1);
public T t2 = new T(2);
public static T t3 = new T(3); public Man(){ // 子类无参构造器
System.out.println("Man()");
}
public Man(int i){ // 子类有参构造器
System.out.println("Man" + "(" + i + ")");
}
}

package com.example;

public class Test{
public static void main(String[] args){
System.out.println("调用子类的无参构造器");
new Man();
System.out.println("调用子类的有参构造器");
new Man(1);
}
}

运行结果:

调用子类的无参构造器
T(4)
T(6)
T(1)
T(3)
T(5)
Person()
T(2)
Man()
调用子类的有参构造器
T(5)
Person()
T(2)
Man(1)
  • 当父类含有无参构造器时,即使子类有多个构造器,这些构造器也无需显式地通过 super 关键字调用父类构造器,因为编译器会自动为每个子类的构造器都调用父类的无参构造器,例如:例子中运行结果打印了两次 Person(),这是两次创建子类对象时,编译器自动调用父类中无参构造器的结果

总结:如果要建立继承关系时,最好给父类创建一个无参构造器或者不给父类创建任何构造器

三、静态块和非静态块

1)静态块会使所有静态属性都被初始化,即使静态属性不在静态块内

例如:


package com.example;

public class T{
public T(int i){
System.out.println("T" + "(" + i + ")");
}
}

package com.example;

public class S{
public static T t1;
public static T t2;
public static T t3;
public T t4;
public T t5;
public T t6;
public T t7 = new T(7); // t7 不在静态块内 static{ // 静态块
t1 = new T(1);
t2 = new T(2);
t3 = new T(3);
} { // 非静态块
t4 = new T(4);
t5 = new T(5);
t6 = new T(6);
}
}

package com.example;

public class Test{
public static void main(String[] args){
System.out.println(S.t1);
}
}

运行结果:

T(7)
T(1)
T(2)
T(3)
com.example.T@15db9742
  • 可以看到,该例子中只是调用了 S.t1 ,静态属性 t2, t3, t7 也跟着被初始化了

四、初始化的方式

1)在定义处初始化

2)在构造器内初始化

3)在静态块或非静态块内初始化

4)等到要用的时候再初始化

五、数组初始化

数组有三种初始化的方式

第一种初始化的方式


package com.example;
import java.util.Arrays; public class Test{
public static void main(String[] args){
int[] a = {1,2,3,4,5}; /* error, 只能写在同一行 int[] a;
a = {1,2,3,4,5} */ // System.out.println(a); 这种方式无法打印数组的值 System.out.println(Arrays.toString(a)); // 打印数组的值
}
}

运行结果:

[1, 2, 3, 4, 5]

第二种初始化的方式


package com.example;
import java.util.Arrays; public class Test{
public static void main(String[] args){
int[] a;
a = new int[5];
for(int i=0; i

运行结果:

[0, 1, 2, 3, 4]

第三种初始化的方式

例1:


package com.example;
import java.util.Arrays; public class Test{
public static void main(String[] args){
int[] a;
a = new int[]{1,2,3,4,5};
System.out.println(Arrays.toString(a));
}
}

运行结果:

[1, 2, 3, 4, 5]

例2(这种方式的用途):


package com.example;
import java.util.Arrays; public class T{
public void show(Integer[] i){ //必须为 Integer 类型,不能是 int 这些基本类型
System.out.println(Arrays.toString(i));
}
public void show(String[] str){
System.out.println(Arrays.toString(str));
}
}

package com.example;

public class Test{
public static void main(String[] args){
T t1 = new T();
t1.show(new Integer[]{5,4,3,2,1}); // 必须为 Integer 类型,不能是 int 这些基本类型
t1.show(new String[]{"张三", "李四", "王五"});
}
}

运行结果:

[5, 4, 3, 2, 1]
[张三, 李四, 王五]
  • 很明显这种方式的优点是无需额外定义一个数组变量 int[] a

参考资料:

《Java 编程思想》第4版


End~

Java笔记 —— 初始化的更多相关文章

  1. Java笔记: 初始化块

    Java语言提供了很多类初始化的方法,包括构造器.初始化器等.除了这两种方法之外,我们还可以用初始化块(initialization block)来实现初始化功能. 基本语法 初始化块是出现在类声明中 ...

  2. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  3. java笔记整理

    Java 笔记整理 包含内容     Unix Java 基础, 数据库(Oracle jdbc Hibernate pl/sql), web, JSP, Struts, Ajax Spring, E ...

  4. 转 Java笔记:Java内存模型

    Java笔记:Java内存模型 2014.04.09 | Comments 1. 基本概念 <深入理解Java内存模型>详细讲解了java的内存模型,这里对其中的一些基本概念做个简单的笔记 ...

  5. Java笔记 —— 继承

    Java笔记 -- 继承 h2{ color: #4ABCDE; } a{ text-decoration: none!important; } a:hover{ color: red !import ...

  6. Java笔记---枚举类和注解

    Java笔记---枚举类和注解 一.枚举类 自定义枚举类 方式一:JDK5.0之前自定义枚举类 class Seasons { //1. 声明Seasons对象的属性 private final St ...

  7. Java类初始化

    Java类初始化 成员变量的初始化和构造器 如果类的成员变量在定义时没有进行显示的初始化赋值,Java会给每个成员变量一个默认值 对于  char.short.byte.int.long.float. ...

  8. Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序

    静态代码块:用staitc声明,jvm加载类时执行,仅执行一次构造代码块:类中直接用{}定义,每一次创建对象时执行.执行顺序优先级:静态块,main(),构造块,构造方法. 构造函数 public H ...

  9. java数组初始化

    java数组初始化 //静态初始化数组:方法一 String cats[] = new String[] { "Tom","Sam","Mimi&qu ...

随机推荐

  1. 二分图【洛谷P2175】 小Z的游戏分队

    P2175 小Z的游戏分队 小Z受不了寂寞,准备举办一次DOTA比赛,为了能让ACM班全部都参加比赛,他还特制了一张DOTA地图能够支持任意多人打任意多人. 现在问题来了,怎么把这么多人分成两队?小Z ...

  2. iOS沙盒文件目录

    iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么.documents,tmp,app,Library.(NSHomeDirectory() ...

  3. Git 的简单测试

    Git 简介 Git(读音为/gɪt/.)是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理. Git 是 Linus Torvalds 为了帮助管理 Linux 内核开 ...

  4. 5月 28日css前端知识

    a:link {color : #FF0000}    #未访问连接时设置颜色 a:visited {color:  #FF0000}   #访问过得连接设置颜色 a:hover {color: #F ...

  5. Java高级工程师应该掌握的东东

    今天偶然看了膜拜单车官网对java程序员的招聘要求,如下,可以对照发现自己的不足 职责 负责APP SERVER中间层等模块开发 完成各类需求开发任务,同时保证服务稳定性.茁壮性 要求 精通Java语 ...

  6. 安装pyautogui时报错备注

    python3.6用pip安装pyautogui时报错,找了蛮多方法都不行,最后通过安装低版本的pyautogui解决,这里备注下 报错图 解决方法: pip install pyautogui==0 ...

  7. Git简单上传和下载

    本文参考 git-简易指南 编写 上传本地代码到gitHub仓库 第一步:建立git仓库 cd到你的本地项目根目录下,执行git命令 git init 第二步:将项目的所有文件添加到仓库中 git a ...

  8. MQTT学习之一

    一MQTT特性: 基于C/S,发布订阅(发布者服务器->云平台代理->订阅客户端)一对多结构,适用于低带宽高延时,基于TCP/IP之上.

  9. CharacterController控制的物体移动

    CharacterController控制的物体移动: public class playerMove  : MonoBehaviour { public float Speed; private C ...

  10. IO流等学习笔记

    1.为什么日期的开始是从1970年0101开始记录,计算机的日期记录是现在的时间距1970年的时间,可正可负.? 2.引用类型默认都为null,基本数据类型为0,除基本数据类型外所有的都为引用数据类型 ...