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);
}
@Override
public 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.延遲初始化(惰性初始化)在使用對象之前
@Override
public 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();//打印33
GrandFather 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》讀書筆記