一、回到前言那一章,关于复用,java中直接提供的两种方式:组合和继承
1.关于组合:类中创建新的类的对象
关于这一点,组合是类似积木的方式,其实相比于继承来说松耦性更好,在了解spring后,会知道spring提出的控制反转,对于这种积木方式又是进一步解耦。
2.关于继承,这是java中的一个特点,同时也是多态的基础
1)注意一个细节
1 public class Test extends Check{ 2 String s ; 3 public static void main(String[]args) { 4 Test t = new Test(); 5 t.f(); 6 } 7 @Override 8 public void f() { 9 f();//这里的原意是调用父类f(),但是忘记写super 10 } 11 } 12 13 class Check{ 14 public void f() { 15 System.out.println("我是父类的f()"); 16 } 17 }
结果如下:
因为忘记写super,形成无限迭代调用当前子类的f(),报错。
2)继承中的创建对象:创建子类对象会先创建其父类对象
注意下例:
1 package com.zk.ant; 2 3 class Father { 4 int i; 5 public Father(int i) { 6 this.i = i; 7 } 8 } 9 10 class Son extends Father {//这里报错 11 12 } 13 14 public class Test { 15 public static void main(String[]args) { 16 } 17 }
这里Son处,编译会报错,因为父类只写了有参构造方法,但是子类没有写,子类默认采用无参构造方法,而子类无参构造方法又去默认调用父类的无参构造方法,发现找不到父类的无参构造方法,所以编译期间就报错。
3)关系到继承的情况下,在清理的时候需要注意:(可以参见第三篇创建与清理)
子类的finalize()方法如果要重写,需要在重写的方法里面显式调用父类 super.finalize()
4)子类中方法,如果是和父类中某个方法同名但是参数类型、个数不同,则构成重载关系;
子类中方法如果完全和父类中某个方法相同,则构成覆盖关系
基于这一点,如果想要在子类中覆盖某个父类方法,最好加上@Override,避免不小心名字写错了造成不必要的麻烦
二、关于代理(关于代理模式的详解,放在下一章节)
在java的语言层次,直接提供的复用类关系就是组合和继承。在它们之间,有一种代理关系, 代理将一个成员对象置于所要构造的类中(这点就像组合),同时又在新类中暴露原来的类的方法(这点就像继承),见下例:
1 public class Test extends A { 2 A a = new A();//将A对象插入到Test中作为成员变量,这点类似组合 3 @Override 4 public void f() {//可以看到Test继承A,暴露出A的方法 5 a.f(); 6 //可以新增拓展功能 7 } 8 } 9 10 class A{ 11 public void f() { 12 System.out.println("ff"); 13 } 14 }
三、java中权限
同类 | 同包 | 父子 | 无关系 | |
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
defualt | Y | Y | N | N |
private | Y | N | N | N |
上面是java中的四种访问修饰符及权限关系,注意的两点:
对于类来说,只能用public和default;
对于继承来说,子类会继承父类的全部元素,包括私有元素,只是子类不能够使用父类的私有元素
1. 在继承中,权限的作用域只能够放大或者不变,不能变小
1 public class Test{ 2 public static void main(String[]args) { 3 B b = new B(); 4 b.f(); 5 } 6 } 7 8 9 class B extends A{ 10 public void f() { 11 System.out.println("B类中的重载方法"); 12 } 13 } 14 15 class A { 16 protected void f() { 17 System.out.println("A类中的重载方法1"); 18 } 19 }
结果如下:
可以看到,B中的f方法将权限扩大了,还是会覆盖原来的A中的f() (仅限于default,protected,public)
需要注意的是,private的方法,默认就是final的,它不能够被继承
四、关于final关键字
1.数据用final修饰
用final修饰的数据,有两个地方:常量 ,static final 修饰 ,不想被修改的量,参数列表中加final
final修饰的数据,必须在使用前赋值(一般来讲是要声明时候就赋值,如果能够保证一定被赋值到,则可以在声明时不赋值),见下例:
1 public class Test{ 2 private final int i=0;//一般来讲,final修饰的声明时候最好赋值 3 private final int j ;//这里声明的时候没有赋值 4 private final Poppet p; 5 6 public Test(){ 7 j = 1;//因为在构造方法中给j赋值了,能够保证使用前已经被赋值 8 p = new Poppet(1); 9 System.out.println("Test()构造方法"+j); 10 } 11 12 public Test(int i) { 13 j = i; 14 p = new Poppet(i); 15 System.out.println("Test(i)构造方法"+j); 16 } 17 public static void main(String[]args) { 18 new Test(); 19 new Test(2); 20 21 } 22 } 23 24 25 class Poppet { 26 private int i; 27 Poppet(int i1) { 28 this.i = i1; 29 } 30 }
上面的例子,如果注释掉 j = 1; 这一句,就会报错
结果如下:
再看一个关于final修饰的参数的例子:
1 public class Test{ 2 public static void main(String[]args) { 3 Test t = new Test(); 4 t.f(2); 5 Poppet p = new Poppet(22); 6 7 p.pp();//打印p中的i 8 t.t(p);//修改p中的i 9 p.pp();//再打印p中的i 10 11 } 12 13 public void f(final int i) { 14 System.out.println(i); 15 //i = 2;//报错,这里不能够修改i 16 } 17 18 public void t(final Poppet p) { 19 //p = new Poppet();//这里报错,因为p 被final修饰,不能够被改变 20 p.i = 3333;// 这里不会报错,因为被final修饰的p本身还是没变的,仍然指向那个对象,变化的是那个对象里面的i,那就跟final没直接关系了 21 22 } 23 } 24 25 26 class Poppet { 27 public int i; 28 Poppet(int i1) { 29 this.i = i1; 30 } 31 public void pp() { 32 System.out.println(i); 33 } 34 }
2.类用final修饰
用final修饰的类不能够被继承