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. 洛谷4316 绿豆蛙的归宿(DAG递推/概率dp)

    题目大意: 给定一个DAG,求起点到终点的路径长度期望 根据题意可以知道每一条边都有一定概率被走到 那么\(\displaystyle\begin{aligned} Ans = \sum_{e \in ...

  2. NSArray,NSMutable和NSSet,NSMutableSet和NSDictionary,NSMutableDictionary用法

    开始编写应用程序的代码时,可以利用大量的 Objective-C 框架.其中,为所有应用程序提供基本服务的 Foundation 框架尤为重要.Foundation 框架包括表示基本数据类型的值类(如 ...

  3. EXPEDI - Expedition 优先队列

    题目描述 A group of cows grabbed a truck and ventured on an expedition deep into the jungle. Being rathe ...

  4. kuangbin专题十六 KMP&&扩展KMP POJ3080 Blue Jeans

    The Genographic Project is a research partnership between IBM and The National Geographic Society th ...

  5. springcloud系列五 feign远程调用服务

    一:Feign简介 Feign 是一种声明式.模板化的 HTTP 客户端,在 Spring Cloud 中使用 Feign,可以做到使用 HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完 ...

  6. 商品录入功能v1.0【持续优化中...】

    # 录入商品 def goods_record(): print("欢迎使用铜锣辉的购物商城[商品管理][录入商品]".center(30, "*")) whi ...

  7. Python的主要应用领域及应用场景

    参考链接:https://www.cnblogs.com/kaid/p/9016673.html 正文: Python简介 Python(英国发音:/ˈpaɪθən/美国发音:/ˈpaɪθɑːn/), ...

  8. POJ3041轰炸行星(匈牙利算法 最小覆盖点集)

    Asteroids Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 25232   Accepted: 13625 Descr ...

  9. Experimental Educational Round: VolBIT Formulas Blitz K

    Description IT City company developing computer games decided to upgrade its way to reward its emplo ...

  10. Unix shell判断和比较

    1.  shell 的$! ,$?, $$,$@ $n        $1 the first parameter,$2 the second... $#        The number of c ...