首页 > 编程 > Java > 正文

《Thinking In Java》复用类

2019-11-08 03:17:04
字体:
来源:转载
供稿:网友

《Thinking In java》复用类

组合与继承

组合:在新的类中产生现有类的对象。继承:按照现有类的类型来创建新类,无需改变现有类的形式,采用现有类的形式并在其中添加新的代码。

组合:

public class TestDemo1 {public static void main(String[] args) { A a = new A(); a.f1();}}class A{public void f1(){B b = new B("哈登");//组合b.f2();System.out.PRintln(b);// 如果不覆写B类中的toString(),那么就会输出b对象的地址}}class B{private int age;private String name ;public B(String name){this.name = name;this.age=28;}public void f2(){System.out.println("姓名为:"+name+" ,年龄为:"+age);}@Overridepublic String toString() {return name;}}

打印结果

这里在B类的构造方法中初始化了age的值,如果将this.age=28注释掉,那么年龄为0.

关于初始化

1.类中域为基本类型时,默认自动初始化为0

2.类中域为引用类型时,默认自动初始化为null

初始化位置

1.在定义对象的地方。意味着在调用构造器之前进行初始化

2.在类的构造器中

3.实例初始化

4.延迟初始化(惰性初始化)在使用对象之前

看一下具体代码

public class ChuShiHua {public static void main(String[] args) {Student student = new Student("Messi");System.out.println(student);}}class Student {//1.在定义对象的地方。意味着在调用构造器之前进行初始化 private String name1="Ronaldo"; private String name2,name3,name4; //2.在类的构造器中 public Student(String name2){ this.name2 = name2; } // 3.实例初始化{name3="Alice";} // 4.延迟初始化(惰性初始化)在使用对象之前@Overridepublic String toString() { if(name4==null){ name4="Peter"; }return name1+" "+name2+" "+name3+ " "+ name4;}}

继承

class Fu{public Fu(){System.out.println("这是父类构造");}}class Son extends Fu{public Son(){System.out.println("这是子类构造");}}public class JiCheng {public static void main(String[] args) { new Son();}}

这里Son类继承了Fu类。关键字是extends.无论如何,父类构造总是先被初始化,然后再初始化子类构造。

子类默认调的是父类的无参构造,如果想要调用父类的有参构造,那么在子类构造的第一句话写上super(..)

class Fu{int age;public Fu(){System.out.println("aaa");}public Fu(int age){this.age = age;System.out.println("这是父类构造"+this.age);}}class Son extends Fu{int i;public Son(int i){**super(20);**this.i = i ;System.out.println("这是子类构造"+ i);}}public class JiCheng {public static void main(String[] args) { new Son(10);}}
protected关键字
对于类的用户而言,是私有的对于其子类或者同一包的类而言,是可以访问的

在F类中定义了一个protected修饰的方法,在Z类中可以访问的。

向上转型

继承的最大用处并不是复用类,而是多态。多态又是基于继承的。

class Instrument{static void play(Instrument e){System.out.println("通过向上转型"+e);}}public class Wind extends Instrument{public static void main(String[] args) {Wind wind = new Wind();Instrument.play(wind);}}

在play()方法中,将Wind对象传入到父类Instrument的play()方法中,照样能够打印出Wind对象。用父类的引用去接收子类的对象,其指向的是子类对象。

final关键字

final所描述的对象是无法改变的。主要用在类,方法,数据中。

数据

告知编译器这块数据是无法改变的。

1.一个永不改变的编译时常量—减轻了运行时的负担

2.一个运行时被初始化的值,而你并不需要它被改变

3.一个即是static 又是 final 的数据描述的是只占据一块不能改变的存储空间

4.当final作用在基本类型的时候,指的是数值无法改变。当fianl作用于对象的引用时,指的是对象的引用不变,但对象本身还是可以改变的。

class FinalData{int num = 1; public final int age1 =20; /* * 1.用static final 修饰的变量应该字母全部大写 * 2.用public可以无限制被访问 * 3.static:强调只有一份 * 4.final: 说明它是一个常量 * * */ public static final int AGE = 10;}public class FinalTest {public static void main(String[] args) {FinalData fd = new FinalData();System.out.println(fd.age1);//输出20// fd.age1=30; 由于age是final所修饰的基本类型,数值不能被改变。 final FinalData fdFinal = new FinalData(); fdFinal.num=30;System.out.println(fdFinal.num);//打印30 //无法编译,因为fdFinal的引用是不能被改变的 // fdFinal = new FinalData();}}
final参数
class FinalData{public void fun(final int i){ // i++;无法编译,因为i是final类型,值无法改变}public void fun2(final FinalData f){f.fun(1); // f = new FinalData(); 无法编译,因为f的引用不可变}}public class FinalTest {public static void main(String[] args) {}}
final方法

作用:

把方法锁定,防止任何继承类修改它的含义效率

类中所以的private方法都是final类型的,由于无法取用private方法,所以也就无法覆盖它。

class GrandFather{private final void fun(){System.out.println("11");}}class Father extends GrandFather{private final void fun(){System.out.println("22");}}class Son extends Father{public final void fun(){System.out.println("33");}}public class FinalMethodTest {public static void main(String[] args) { Son s = new Son(); s.fun();//打印33GrandFather f = s;//f.fun(); 无法编译 }}

类内所有 private 方法都自动成为 final。由于我们不能访问一个 private 方法,所以它绝对不会被其他方 法覆盖(若强行这样做,编译器会给出错误提示)。可为一个 private 方法添加 final 指示符,但却不能为 那个方法提供任何额外的含义。

final类

final类描述的是该类没有子类,无法被继承。

可以看到上面代码报错,无法编译,原因是父类被声明为final类型。

在一个类中如果被声明为final类型,那么该类的方法隐式的都被声明为final类型。

初始化及类的加载

class Fu {private int age =70;private String name;public static String address = "崇明岛";static {System.out.println("这是父类的static静态块");}public Fu(){System.out.println("这是父类构造");}public void fu(){System.out.println(age+name+address);}}class Zi extends Fu{private int age=30 ;private String name;public static String address = "海南岛";static {System.out.println("这是子类的static静态块");}public Zi(){System.out.println("这是子类构造");}public void zi(){System.out.println(age+name+address);}}public class CSHTest extends Zi{private int age=10 ;private String name;public static String address = "台湾岛";static {System.out.println("这是孙类的static静态块");}public CSHTest(){System.out.println("这是孙类构造");}public void test(){System.out.println(age+name+address);}public static void main(String[] args) { CSHTest t = new CSHTest(); t.test();}}

首先执行CSHTEST类的main方法,类加载器查找CSHTEST编译后的代码.class,发现其有一个父类Zi,于是加载Zi,发现它又有一个父类,于是再加载Fu,此时,根基类中的static初始化(static修饰符描述的方法只执行一次),然后是下一个导出类,以此类推。此种方式很重要。因为导出类初始化有可能依赖于父类的初始化。

此时类已经加载完毕了。现在可以创建对象了。首先,对象中的基本类型都被初始化为0,对象的引用都被初始化为null,然后,基类的构造器就被调用,在本例中,是默认调用的,默认调用super(),如果想指定调用有参构造,那么自己写super(Type Args).

总结就是:加载父类的静态块—>加载子类静态块—->加载父类的构造—>加载子类构造

声明

所写内容是《Thinking In Java》读书笔记


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表