Java复用类

Java代码复用

第一种 只需在新的类中产生现有类的对象,这种方式称为组合,该方法只是复用了现有程序代码的功能。

第二种 按照现有类的类型创建新类,无需改变现有类的形式,采用现有类的形式并在其中添加新代码。这种方式称为继承, 继承是面向对象程序设计的基石之一。

组合和继承,语法和行为大多是相似的。

1 组合

组合只需要将一个要使用的类的对象置于新类中即可。

类中域为基本类型时能自动被初始化为零,但对象引用会被初始化为null,这个时候调用这个对象的任何方法都会返回一个运行时异常。

编译器不是为每一个引用都创建默认的对象,如果那样会增加没必要的系统负担,如果想初始化这些引用,可以在以下位置进行:

     1 在定义对象的地方。这样他们总是在构造器调用之前初始化。

     2 在类的构造器中。

     3 在使用这些对象之前,这种方式被称为惰性初始化。在生成对象不值得及不必每次都生成对象的情况下可以减少系统负担。

     4 使用实例初始化。

2 继承

Object 类是java中所有类的父类,创建一个类时如果不显式的指定继承某个类则默认继承object类

基本语法 class A{}

class B extend A {} A称为B的父类,超类,基类

通过extend关键字来表示类B继承自类A,当B继承自A时,会自动获得A中的所有域和方法。

当一个B对象调用其方法时,如果这个方法是继承自A且B没有进行覆写,则直接调用A中的方法,如果B进行了覆写,则调用B覆写的此方法;当B覆写此方法时想要调用A中的此方法则可以使用super关键字,当B中重载了此方法切未进行覆写时,会自动根据参数列表的不同调用不同版本的方法。

进行继承时A中的所有方法都必须是public 的 ,因为如果从其他包中对A进行继承时,子类只能访问A中的public 属性和方法。所以为了继承,一般选择对方法使用public 属性使用private。特殊情况可以调整。

可以为每个类都设置一个main方法,即使一个程序中含有多个类,也只有被命令行调用的那个类的main方法会被调用,而为其他类增加main方法会使每个类的单元测试更简便。并且在测试后无需删除这个main方法。

2.1 初始化基类

当创建一个子类的对象时,该对象包含了一个父类的对象,这个对象与直接使用父类进行创建 的对象是一样的。区别是后者来自外部而前者被包装在导出类对象内部。

所以在创建一个子类对象时如何对这个子对象进行初始化呢,Java会自动在子类的构造器中插入对父类构造器的调用。无论子类是否显式的定义构造器。在创建子类的对象时总是先调用父类的构造器,且调用顺序是从最底层的那个父类向外扩散的。虽然子类会强制去初始化父类,但只要调用构造方法即可,不会强制将父类的属性也进行初始化。

public class Test {

public static void main(String[] args) {

B b = new B();

System.out.println(b.toString());

}

}

class A{

int i = 0;

public A(){

this.i = 1;

//使用this但引用的是B的对象,自动使用构造器后赋值给B的i

}

}

class B extends A{

@Override

public String toString() {

return "B{" +

"i=" + i +

'}';

}

} // B{i=1}

当A类中不含有无参构造器时,子类构造器必须显式的调用一个与A的构造器,否则编译器会报错,因为Java会自动的调用父类的构造器,当A没有无参构造器时,调用就需要传入一个值,这一步骤就需要使用super()来进行。

总结:在A中不含有无参构造器时,B的构造方法必须显式的使用super来调用A的一个构造器。

3 代理

代理作为代码复用的第三种关系存在,Java并没有提供直接支持。

当使用继承时,会将A类的所有方法暴露给B类,而代理解决了这个问题。

基本使用方式

class A {

public void a(){}

public void b(){}}

class B {

A a = new A();

public void a(){ a.a()}

public static void main(String[] args){

B b = new B();

b.a();

}}

代理可以解决只想暴露给客户端程序员方法接口而不是整个方法时的问题。只对其展示方法调用而不暴露具体实现。

4 名称屏蔽

当父类中存在一个方法被重载多次,在子类中对此方法再次进行重载不会影响任何一个版本。

public class Test {

public static void main(String[] args) {

D d = new D();

d.ww(2d);

d.ww(new B());

d.ww("ss");

d.ww(1);

d.qq(new G());

}

}

class A{

public void ww(int i){

System.out.println("A.WW,int");

}

public void ww(String i){

System.out.println("A.WW,String");

}

public void qq(G b){

System.out.println("A.WW G");

}

}

class B{}

class G{}

class C extends A{

public void ww(B b){

System.out.println("C.WW B");

}

public void qq(G b){

System.out.println("C.WW G");

}

}

class D extends C{

public void ww(double b){

System.out.println("D.WW");

}

}

/*D.WW

C.WW B

A.WW,String

A.WW,int

C.WW G*/

5 protected关键字

前文所说的将父类的属性定义为private可以保证安全但会导致子类也无法访问这个属性,如果需要允许子类访问这个属性,则可以使用protected关键字

对于类用户而言,这是private,但对于子类或同一个包下的其他类来说,他就是可以访问的。

6 向上转型

继承技术中最重要的不是“为新的类提供方法”而是表现新类与基类之间的关系:新类是基类的一种类型。

向上转型是从一个较专用的类型转向一个较通用的类型,所以总是很安全。

子类是父类的一个超集,可能比父类的方法多,但一定包含父类的方法。所以可以发向父类的信息也同样可以发向子类。

7 final 关键字

final可能用到的三种情况:数据 方法 类

1 final数据

有时final数据是很有用的,比如一个永不改变的编译时常量或一个在运行时被初始化的值而且不被改变。

一个既是static又是final的域只占据一段不可更改的存储空间。

当final对对象引用使用时,final的含义是:引用不变,在初始化指向一个对象之后,这个引用就不会再改变,但可以改变这个引用内部的值,比如他的属性。而对基本数据类型使用时,含义是值不变。

一个类中含有一个final属性时,每个对象享有一份不同引用的属性值,但如果不使用空白final这些值将是相同的。

空白final

Java允许使用空白final,即声明为final但没有定初值。这样可以使不同的对象根据情况具有不同的属性值。但无论什么情况编译器都会在使用前初始化这个变量,否则会报错。

final参数

Java允许在参数列表使用final,含义为:你无法在方法内更改这个参数引用所指向的对象,基本数据类型也无法更改值。这一特性多用于向匿名内部类传递数据。

2 final方法

使用final方法有两个原因:一是把方法锁定,防止子类更改他的含义,不会被覆盖。二是为了优化性能。

类中定义的private方法都被隐式的定义为final,对一个privtae方法使用final修饰没有任何额外意义。

class A{

public final void Show(){

System.out.println("A.show");

}

}

class B extends A{

public final void Show(){

System.out.println("B.show");

}

}

当父类定义一个非private的final方法时,子类无法进行覆写,编译器报错:'Show()' cannot override 'Show()' in 'A'; overridden method is final 因为final无法被更改

class A{

private final void Show(){

System.out.println("A.show");

}

}

class B extends A {

public final void Show() {

System.out.println("B.show");

}

}

当父类定义一个private的final方法时,由于子类是无法继承这个方法的所以调用b.show时,输出B.Show。但这并不是覆写,因为子类并没有继承这个方法,只是子类又定义了一个新的方法,与父类的方法无关。

class A{

private final void Show(){

System.out.println("A.show");

}

}

class B extends A {

private final void Show() {

System.out.println("B.show");

}

}

当子类父类的final方法都是private时,可以这样写但无法调用b.show,调用时编译器报错:Ambiguous method call(不明确的方法调用). Both Show() in B and Show() in A match

并且这个方法也是不该被调用的因为他是private的。

这三个例子中 final的存在只是为了明显的举例,实际操作中final没有存在必要。在没有final的情况下结果相同。

3 final类

在类的定义前增加final关键字意味着这个类不需要任何变动。这会导致这个类无法被继承。

final类内部的属性可以根据个人意愿选择是否定义为final,但其方法都被隐式的定义为final因为它们都是无法被覆写的。在final类中可以给方法增加final修饰词,但同样没有任何意义。

8 初始化及类的加载

Java中没个类的编译代码存在于各自的独立文件中,这份文件只有在需要使用程序代码时才会被加载。所以说类的代码在初次使用时被加载,通常是指创建这个类的的一个对象时。但当不创建对象直接访问这个类中的static域或方法时,也会加载这个类。

初次使用某个类的代码时就是这个类中的static初始化的时刻。并且构造器是隐式的定义为static的方法,所以可以说:类是在其任何static部分被调用时加载的。

类的加载只发生一次 public class Test {

public static void main(String[] args) {

A a1 = new A();

A a2 = new A();

}

}

class A{

static int s = print("static");

public static int print(String s){

System.out.println(s);

return 4;

}

}

当子类调用时,子类构造器会先调用父类的构造器导致父类被加载,这时会对父类中含有的static进行初始化,完成父类的被调用的构造器的所有初始化后,返会子类继续进行子类的加载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值