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 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18
fd2: 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关键字一样)。

posted on 2014-10-03 15:08  winlrou  阅读(175)  评论(0编辑  收藏  举报