zoukankan      html  css  js  c++  java
  • 【Java】初始化

    默认域初始化

    1. 如果在构造器中没有显示地给域赋予初值,那么就会被自动赋予默认值:数值为0,布尔值为false,对象引用为null。

    无参数构造器

    很多类都包含一个无参数的构造函数,对象由无参数构造函数创建时,其状态会设置为适当的默认值。例如,如下是Employee的无参数构造函数:

    pubilc Employee() {
        name = "";
        salary = 0;
        hireDay = new Date();
    }
    
    1. 如果编写一个类时没有编写构造器, 那么系统就会提供一个无参数的构造器。这个构造器将所有的实例域设置为默认值。于是,实例域中的数值型数据设置为0、布尔型数据设置为false,所有对象引用将设置为null。
    2. 如果类中至少提供了一个构造器,但是没有提供无参数的构造器,则在构造对象如果没有提供参数就会被视为不合法。

    显示域初始化(指定初始化)

    由于类的构造器方法可以重载,所以可以采用多种形式设置类的实例域的初始状态。确保不管怎么调用构造器,每个实例域都可以被设置为一个有意义的数初值。这是一种很好的设计习惯。
    可以在类的定义中,直接将一个值付给任何域。例如:

    class A{}
    Class Employee {
        private String name = "";
        private A a = new A();
    }
    

    在执行构造器之前先执行赋值操作。当一个类的所有构造器都希望把相同的值赋给某个特定的实例域时,这种方式特别有用。
    初始值不一定是常量。在下面这个例子中,可以调用方法对域进行初始化。Employee类中每个雇员都有一个id域。可以使用下列方式进行初始化:

    class Employee{
        private static int nextId;
         private int id = assignId();
        
        private static int assignId() {
            int r = nextId();
            nextId++;
            return r;
        }
    }
    

    这个方法也可带有参数,但这些参数必须是已经初始化的了。因此,可以这样写:

    public class MethodInit {
        int i = f();
        int j = g(i);
        int f() {return 11;}
        int g(int n ) {return n* 10;}
    

    但像下面这样就不对了:

    public class MethodInit{
        int j = g(i); //Illegal forward reference
        int i = f();
        int f() {return 11;}
        int g(int n) {return n * 1
    

    初始化块

    前面已经讲过两种初始化数据域的方法:

    • 在构造器中设置值
    • 在声明中初始化
      实际上,Java中还有第三种机制,称为初始化块。在一个类的生命中,可以包含多个代码块。只要构造类的对象,这些块就会被执行。例如:
    class Employee{
        private static int nextId;
        
        private int id;
        private String name;
        private double salary;
    
        //object initialization block
        {
            id = nextId;
            nextId++;
        }
    
        public Employee(String n, dobule s) {
            name = n;
            salary = s;
        }
        
        public Employee(){
            name = "";
            salary = 0;
        }
    }
    

    在这个示例中,无论使用哪个构造器构造对象,id域都在对象初始化块中被初始化。首先运行初始化块,然后才运行构造器的主体部分。

    this关键词

    假设你希望在方法的内部获得对当前对象的引用,就可以使用this关键字。this关键字只能在方法的内部使用,表示对“调用这个方法的那个对象”的引用。this的用法和其他对应引用并无不同。只要注意,如果在方法内部调用同一个类的另一个方法,就不必使用this,直接调用即可。

    只有当需要明确指出对当前对象的引用时,才需要使用this关键字。例如,当需要返回对当前对象的引用时,就常常需要在return语句里这样写:

    public class Leaf{
        int i = 0;
        Leaf increment() {
            i++:
            return this;
        }
    }
    
    public static void main(String[] args) {
        Leaf  x = new Leaf();
        x.increment().increment().increment();
    }
    

    由于increment()通过this关键字返回了对当前对象的引用,所以很容易在一条语句中对同一个对象进行多次操作。
    this关键字对于将对于将当前对象传递给其他对象也很有用:

    Class Person{
        public void eat(Apple apple) {
            Apple peeled = apple.getPeeled();
            System.out.println("Yummy");
       }
    }
    
    class Peeler {
        static Apple peel(Apple apple) {
            //... remove peel
            return apple; //Peeled
        }
    }
    
    class Apple{
        Apple getPeeled() {return Peeler.peel(this);}
    }
    
    public class PassingThis {
        public static void main(Strings[] args) {
            new Person.eat(new Apple));
        }
    }
    
    参数名

    构造器的的参数名和引用如下所示:

    Class Employee{
        private String name;
        private double salary;
    
        public Employee(String name , double  salary) {
            this.name = name;
            this.salary = salary;
        }
    }
    

    它基于这样一个事实:参数变量用同样的名字将实例域屏蔽起来。例如,如果将参数命名为salary,salary将引用这个参数,而不是实例域。但是,可以采用this的形式访问实例域。

    调用另一个构造器

    如果构造器的第一个句子形如(this...),这个构造器将调用同一个类的另一个构造器。下面是一个典型的例子:

    public Employee() {
        //calls Employee(String, double)
        this("Employee #" + nextId, s);
        nextId++;
    }
    

    尽管可以用this调用另一个构造器,但却不能同时调用两个。此外,必须将构造器调用置于方法最起始处,否则编译器会报警。


    静态域初始化

    1. 如果一个域是静态的基本类型域,且没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初始值就是null。
    2. 如果想在定义处进行初始化,采取的方法与非静态数据没什么不同
    Class A{
        private static B b = new B();
    }
    

    3.也可以用使用指定初始化,不过要在初始化块的外面加 static关键字

    class Cup{
        Cup(int marker) {
        }
    }
    
    class Cups{
        static Cup cup1;
        static Cup cup2;
        static {
            cup1 = new Cup(1);
            cup2 = new Cup(2);
        }
        Cup() {
            print("Cups()");
        }
    }
    
    public class ExplicitStatic {
        public static void main(Strings[] args) {
            print("Inside main()");
            Cups.cup1.f(99);
        }
        static Cups cups1 = new Cups();
        static Cups cups2 = new Cups();
    }
    

    无论是通过标为(1)的那行代码访问静态的cup1对象,还是把标为(1)的行注释掉,让它去运行标为(2)的那行代码(即解除标为(2)的行的注释),Cups的静态初始化动作都会得到执行。如果把标为(1)和(2)的行同时注释掉,Cups的静态初始化动作就不会进行。此外,激活一行还是两行标为(2)的代码(即解除注释)都无关紧要,静态初始化动作只进行一次。

    数组初始化

    数组初始化主要有两种形式。第一种如下:

    //Aggregate initialization
    BerylliumSphere[] d = { new BerylliumSphere(), new BerylliumSphere(), new BerylliumSphere()};
    

    数组d表明使用"聚集初始化"语法创建数组对象,并且以BerylliumSphere对象将其初始化的过程,但是这个操作必须在定义d的位置使用。

    第二种如下:

    //Dynamic aggregate initialization
    a = new BerylliumSphere[] { 
    new BerylliumSphere(), new BerylliumSphere()
    };
    

    这种方法被看作是"动态的聚集初始化",这种方法可以在任意位置创建和初始化数组对象。例如,假设方法hide()需要一个BerylliumSpere对象的数组作为输入参数。可以如下调用:
    hide(d);
    但也可以动态地创建将要作为参数传递的数组:

    hide(new BerylliumSpere[]{new BerylliumSpere()}
    

    双括号初始化(Java核心技术卷一,P243)

    双括号初始化利用了内部类的语法。假设想构造一个数组列表,并将它传递到一个方法。

    ArrayList<String> friends = new ArrayList<>();
    friends.add("Harrys");
    friends.add("Tony");
    invite(friends);
    

    如果不需要这个数组列表,最好让它作为一个匿名列表。不过作为一个匿名列表,添加元素方法如下:

    invite(new ArrayList<String>(){{add("Harrys");add(""Tony"")}})
    

    注意这里的双括号。外括号建立了Arraylist的一个匿名子类。内括号则是一个对象构造块。

  • 相关阅读:
    累加和最大化
    SELECT子句顺序
    Linux命令入门教程
    求字符串前缀最长的连续重复子串(不重叠)
    王道机试指南题解(C/C++版)
    C++ Primer 第 5 版 习题参考答案
    第 16 章
    第 15 章
    第 14 章
    从jvm源码解读Java运行时的类加载
  • 原文地址:https://www.cnblogs.com/vincently/p/5993829.html
Copyright © 2011-2022 走看看