首页 > 代码库 > Java学习笔记五:复用类

Java学习笔记五:复用类

当我们在使用第三方或者其他人已经写好的类时,通常有两种方法:

(1).组合:在新的类中产生现有类的引用。

(2).继承:按照现有类的类型创建新类。

 

1.组合

简单理解就是新类中,创建一个变量,变量的引用为现有类。

我们在很多场景下都是用到组合,如:

public class Lesson_04_Extend{    private Extend1 extend1; // 组合方式,创建成员变量(UML 组合关系)        public void print(Extend1 extend2) // (UML 依赖关系)    {            }}class Extend1{    }

上述例子例子中还特别说明了下在uml类图中组合和依赖的关系

组合:一般作为变量,是一个与整体关系非常强的表示方式,生命周期与整体一致;

依赖:一般作为方法参数,与整体关系较弱,生命周期随着方法调用结束和结束,意味着与整体关系不这么密切。

 

2.继承

顾名思义,就是创建新类是拥有父类的特性。

当我们创建一个类是,如果没有显式继承某个父类,则会隐式的继承至Object。

(1)当新类继承父类时,就会拥有父类中 public 或者 protect 的属性或者方法。

(2)super 关键字表示超类的意思,主要作用是对父类方法的引用

1.想要使用父类带参数的构造函数时,可以使用super(x);

2.创建新类时,使用super.xxx()使用父类的方法。

public class Lesson_04_Extend{    private Extend1 extend1;         public Lesson_04_Extend(int i)    {        System.out.println("Lesson_04_Extend.Lesson_04_Extend()" + i);    }        public void print(Extend1 extend2)    {        System.out.println("Lesson_04_Extend.print()");    }        public String printStr()    {        return "Lesson_04_Extend";    }}class Extend1 extends Lesson_04_Extend{    public Extend1(int i)    {        super(i); // 父类构造函数        super.printStr(); // 父类方法    }        /**     * 重写父类方法(区别重载)     */    @Override    public String printStr()    {        return "Extend1";     }}

 

3.final关键字

final关键字表示“这是无法改变的”。通常有两个理由不想做出改变:1.设计(最常用的);2.效率(不常用,在jdk6,7不明显)

1.final数据

在编程时,有时数据的恒定不变时很有用的,如:

(1)一个永不改变的编译时常量

(2)一个在运行时被初始化的值,而你不希望他改变

用代码理解,如:

class FinalDemo{    public static final String FINAL_CONSTANT = "final constant"; // 编译时常量        private final String final_init = "final_init"; // 初始值}

 

2.当使用static final 时,系统内存会为我们在栈中开辟一个不能改变的存储空间,通常会使用在基本数据类型和引用上

public static final int INT_STATIC = 100; // 基本数据类型    public static final Lesson_04_Extend lessson_4 = new Lesson_04_Extend(1); // 引用类型

这里说明下:当我们使用引用类型时,不变的是在栈中存储的引用,而易用指向堆中的的数据是可以改变的。

这里举个例子:当我们获取身份证的时候,如果没有特殊情况,这个身份证就永远不会改变(如照片,住址),但是身份证上的人是可以改变的(如短头发,长头发等等)。

 

下面代码用的是“Java编程思想”的例子,由于书上说明太乱,这里是自己理解描述:

class FinalData{    private static Random rand = new Random(47);        private String id;        public FinalData(String id)    {        this.id = id;    }        // Can be compile-time constants:    private final int valueOne = 9;        private static final int VALUE_TWO = 99;        // Typical public constant:    public static final int VALUE_THREE = 39;        // Cannot be compile-time constants:    private final int i4 = rand.nextInt(20);        static final int INT_5 = rand.nextInt(20);        private Value v1 = new Value(11);        private final Value v2 = new Value(22);        private static final Value VAL_3 = new Value(33);        // Arrays:    private final int[] a =    {        1, 2, 3, 4, 5, 6    };        public String toString()    {        return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;    }        public static void main(String[] args)    {        FinalData fd1 = new FinalData("fd1");        // ! fd1.valueOne++; // Error: can‘t change value        fd1.v2.i++; // Object isn‘t constant!        fd1.v1 = new Value(9); // OK -- not final        for (int i = 0; i < fd1.a.length; i++)            fd1.a[i]++; // Object isn‘t constant!        // ! fd1.v2 = new Value(0); // Error: Can‘t        // ! fd1.VAL_3 = new Value(1); // change reference        // ! fd1.a = new int[3];        System.out.println(fd1);        System.out.println("Creating new FinalData");        FinalData fd2 = new FinalData("fd2");        System.out.println(fd1);        System.out.println(fd2);    }}

输出结果:

fd1: i4 = 15, INT_5 = 18Creating new FinalDatafd1: i4 = 15, INT_5 = 18fd2: i4 = 13, INT_5 = 18

(1)首先,肯定先执行 main 函数,在 main 函数中,初始化 FinalData , 初始化结果:

1.1 下面三个先按顺序执行,同时初始化 VAL_3 

private static final int VALUE_TWO = 99;public static final int VALUE_THREE = 39;private static final Value VAL_3 = new Value(33);

这里说明下:上面三个值是由于是在编译时就知道它们的值,所以在定义变量时需要用到大写+下划线;

1.2 初始化其他非static成员变量

这里说明下,final 前面加上static 和 不加 static 的区别:就初始化而言,执行的先后顺序有差别而已。

private final int valueOne = 9;private static final int VALUE_TWO = 99;

1.3 构造函数初始化,将id 赋值给成员变量。

(2)执行fd1;

2.1 执行v2.i++ 

fd1.v2.i++;  // 值是23

如果这个时候声明,编译报错

// fd1.v2 = new Value(22); // Error, final Data

2.2 引数组也是引用,指向引用内容的值是可以改变的(只是指向数据的引用不能在指向其他引用)

数组值为:

{ 2, 3, 4, 5, 6, 7};

(3)声明fd2,静态成员不会再次初始化,这里注意的是i4的值,由于fd1,fd2是两个不同的引用,在堆中有两个不同的空间,

所以,在执行i4的时候有两个值,而 i5只有一个(静态。)

 

3.空白final

指被声明为final担忧为给定初始值的域。

可以在构造函数,或者方法调用时给final指定的对象附上初始值

class Poppet{}class BlankFinal{    final int i = 0; // Initialized final        final int j; // Blank final        final Poppet p; // Blank final handle    // Blank finals MUST be initialized    // in the constructor:        BlankFinal()    {        j = 1; // Initialize blank final        p = new Poppet();    }        BlankFinal(int x)    {        j = x; // Initialize blank final        p = new Poppet();    }        public static void main(String[] args)    {        BlankFinal bf = new BlankFinal();    }}

 

4.final参数

在方法中指定final,表明方法中指定的引用时不能改变的。

class FinalArgument{    void with(final BlankFinal g)    {        // ! g = new Gizmo(); // Illegal -- g is final    }        void without(BlankFinal g)    {        g = new BlankFinal(); // OK -- g not final    }}

 

5.final类

当类使用final时,表明不打算让其他类继承这个类(与C#中的sealed关键字一样)。

Java学习笔记五:复用类