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. window安装配置 zookeeper 单机模式

    1.zookeeper简单介绍 zookeeper是一个分布式的,开放源码的分布式应用程序协调服务,我所了解到的主要的使用场景有两个 (1).微服务注册中心(Dubbo较常用Zookeeper做注册中 ...

  2. Eclipse 创建Maven 项目

    https://www.cnblogs.com/noteless/p/5213075.html

  3. CF1101C Division and Union 线段相交问题

    #include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> #i ...

  4. UVa 11292 勇者斗恶龙(The Dragon of Loowater)

    首先先看一下这道题的英文原版... 好吧,没看懂... 大体意思就是: 有一条n个头的恶龙,现在有m个骑士可以雇佣去杀死他,一个能力值为x的勇士可以砍掉直径不超过x的头,而且需要支付x个金币.如何雇佣 ...

  5. java:数据结构复习(三)链表队列

    @TOC 和栈一样,队列也是表,但是使用队列的特点是先进先出. 队列模型 队列的基本操作是入队,它是在表的末端插入一个元素,和出队,它是删除在表开头的一个元素 graph LR A[<kbd&g ...

  6. Jmeter函数引用和函数重定向【转】

    在jmeter中的[选项]中选择[函数助手对话框]---这些函数可以高速有效的帮助我们开展自动化编写与校验!!!!!! 如图: 重点!!!本章的侧重点不讲函数的具体使用,函数具体的使用与java类似, ...

  7. github 第三方登录

    第三方登录先了解 OAuth 2.0 OAuth 协议的认证和授权的过程如下: 用户打开我的博客后,我想要通过GitHub获取改用户的基本信息 在转跳到GitHub的授权页面后,用户同意我获取他的基本 ...

  8. 【ABP开发】:asp.net core 中使用mysql

    EntityFrameworkCore项目--Nuget包管理,卸载包: Microsoft.EntityFrameworkCore.SqlServer: EntityFrameworkCore项目和 ...

  9. Luogu P5122 [USACO18DEC]Fine Dining 最短路

    先跑一遍n为起点最短路,再新开一个点,向有干草垛的点连一根边权为d[u]-w的有向边(很重要..我当时连的无向边,然后我死了.),相当于用价值抵消一部分边权, 然后以这个新的点为起点跑最短路就好了.. ...

  10. 练习五十七:for循环 809??=800*?+9*?+1其中?代表的两位数,8*?的结果为两位数,9*?的结果为3位数。求?代表的两位数,及809??后的结果

    题目:809??=800*?+9*?+1其中?代表的两位数,8*?的结果为两位数,9*?的结果为3位数.求?代表的两位数,及809??后的结果 注意:一定要看清楚题目哦,809??代表的是结果,?代表 ...