Java面向物件
初識面向物件
面向程序 & 面向物件
面向程序思想
- 步驟清晰簡單,第一步做什么,第二部做什么...
- 面對程序適合處理一些較為簡單的問題
面向物件思想
- 物以類聚,分類的思維模式,思考問題首先會解決問題需要哪些分類,然后對這些分類進行單獨思考,最后,才對某個分類下的細節進行面向程序思索,
- 面向物件合適處理復雜的問題,適合處理需要多人協作的問題!
對于描述復雜的事物,為了從宏觀上把握、從整體上合理分析,我們需要使用面向物件的思路來分析整個系統,但是,具體到微觀操作,仍然需要面向程序的思路去處理,
什么是面向物件
面向物件編程(Object-Oriented Programming,OOP)
面向物件編程的本質是:以類的方式組織代碼,以物件的組織(封裝)資料
抽象:白話理解就是抽像,把像的的抽取出來,具體的理解還是抽像
三大特征:
- 封裝
- 繼承
- 多型
從認識論角度考慮是先有物件后有類,物件,是具體是事物,類,是抽象的,是對物件的抽象
從代碼運行角度考慮是先有類后有物件,類是物件的模板
這些看不懂沒關系,后面慢慢的就會恍然大悟!
回顧方法及加深
? 一個真正的程式里面只有一個main方法,并不會像我們測驗的時候每個類里面都有,我們只是為了方便代碼的運行
回顧方法的定義
-
修飾符
- public:公共的,所有人都可以呼叫這個方法
- static:靜態,方便呼叫 (可選)
-
回傳型別
- void:空的:不需要使用return回傳一個值(但是可以使用return結束方法)
- 其他型別:需要使用return回傳一個值,回傳的值的型別要與其相對應
-
break和return的區別
- break:跳出switch陳述句,和結束整個回圈;還有一個continue是結束一次回圈
- return:結束當前方法,回傳一個結果(可以為空)
-
方法名
- 注意規范即可:見名知意,首字母小寫+駝峰原理
-
引數串列
- (引數型別 引數名,...) 可以多個,還有一個 可變引數(引數型別... 引數名)
package com.xiaodi.operator.oop.demo01;
//Demo01 類
public class Demo01 {
//main 方法
public static void main(String[] args) {
}
/*
方法的定義:
修飾符 回傳值型別 方法名(...) {
//方法體
return 回傳值;
}
*/
public String sayHello() {
return "Hello,World";
}
public double max(double a, double b) {
return a > b ? a : b; //三元運算子
}
}
- 例外拋出
- 疑問 (后面講解)
回顧方法的呼叫
-
靜態方法
- 靜態方法:加static的為靜態方法
package com.xiaodi.operator.oop.demo01; //學生類 public class Sudent { //靜態方法 static public static void say() { System.out.println("學生說話"); } }- 靜態方法的呼叫
package com.xiaodi.operator.oop.demo01; public class Demo02 { public static void main(String[] args) { //靜態方法呼叫:類名.方法名; Sudent.say(); } } -
非靜態方法
- 非靜態方法:沒加static的為非靜態方法
package com.xiaodi.operator.oop.demo01; //學生類 public class Sudent { //非靜態方法 public void say() { System.out.println("學生說話"); } }- 非靜態方法的呼叫
package com.xiaodi.operator.oop.demo01; public class Demo02 { public static void main(String[] args) { //非靜態方法呼叫 //先實體化這個類 new ;物件型別 物件名 = new 物件值; Sudent sudent = new Sudent(); //然后呼叫 sudent.say(); } }-
特殊情況 原因:static 是和類一起加載的,這個類存在的時候它就存在,時間片非常早;普通方法是類實體化后才存在的
- 兩個普通方法(非靜態) 或 兩個靜態方法:可以直接相互呼叫,無需實體化
package com.xiaodi.operator.oop.demo01; public class Demo02 { public static void main(String[] args) {} //兩個普通方法(非靜態):可以直接相互呼叫,無需實體化 public void a() { b(); } public void b() {} public static void c() { d(); } public static void d() {} }- 兩個方法中一個一個為普通方法,一個為靜態方法,如果靜態方法呼叫非靜態,就要實體化;非靜態呼叫靜態無須實體化
package com.xiaodi.operator.oop.demo01; public class Demo02 { public static void main(String[] args) {} public static void a() { Demo02 demo02 = new Demo02(); demo02.b(); } public void b() {} }
-
形參和實參:形式引數和實際引數的型別要對應
- 形式引數:是定義方法時的引數;例如:如下代碼的(int a, int b)就是形式引數
package com.xiaodi.operator.oop.demo01; public class Demo03 { public static void main(String[] args) { } public int add(int a, int b) { return a+b; } }- 實際引數:是呼叫方法傳遞給方法的引數;例如:如下代碼的demo03.add(1, 2);
package com.xiaodi.operator.oop.demo01; public class Demo03 { public static void main(String[] args) { Demo03 demo03 = new Demo03(); demo03.add(1,2); } public int add(int a, int b) { return a+b; } } -
值傳遞和參考傳遞
-
值傳遞參考傳遞的概念
- 值傳遞是指在呼叫方法時將實際引數復制一份傳遞到方法中,這樣在方法中如果對引數進行修改,將不會影響到實際引數,
package com.xiaodi.operator.oop.demo01; //值傳遞 public class Demo04 { public static void main(String[] args) { int a = 1; System.out.println(a); //1 Demo04.change(a); System.out.println(a); //1 } //回傳值為空 public static void change(int a) { a = 10; } }- 所謂參考傳遞是指在呼叫方法時將實際引數的地址傳遞到方法中,那么在方法中對引數所進行的修改,將影響到實際引數,
package com.xiaodi.operator.oop.demo01; //參考傳遞:java本質還是值傳遞 public class Demo05 { public static void main(String[] args) { Perosn perosn = new Perosn(); System.out.println(perosn.name);//nell Demo05.change(perosn); System.out.println(perosn.name);//曉迪 } public static void change(Perosn perosn) { perosn.name = "曉迪"; } } //定義了一個Perosn類,有一個屬性:name class Perosn { String name;//null } -
值傳遞和參考傳遞的理解
- 值傳遞 在方法的呼叫程序中,實參把它的實際值傳遞給形參,此傳遞程序就是將實參的值復制一份傳遞到方法中,這樣如果在方法中對該值(形參的值)進行了操作將不會影響實參的值,因為是直接復制,所以這種方式在傳遞大量資料時,運行效率會特別低下,
- 參考傳遞 參考傳遞彌補了值傳遞的不足,如果傳遞的資料量很大,直接復過去的話,會占用大量的記憶體空間,而參考傳遞就是將物件的地址值傳遞過去,方法接收的是原始值的首地址值,在方法的執行程序中,形參和實參的內容相同,指向同一塊記憶體地址,也就是說操作的其實都是源資料,所以方法的執行將會影響到實際物件,
-
java本質還是值傳遞(為什么這么說:參考傳遞是里面存的首地址的copy,原來參考變數里的的物件你不能改,但是他指向的堆物件你可以改啊,)
-
結論:
- 值傳遞,相當于拷貝一份值,對引數的修改不影響原有引數
- 參考型別傳參考,形參和實參指向同一個記憶體地址(同一個物件),所以對引數的修改會影響到實際的物件,
-
上面還寫了一個class(劇透了內部類的內容(后面會講)) 一個類里面只能有一個public class 但是能有多個class
- this關鍵字 (講到繼承的時候再講)
值傳遞和參考傳遞大家肯定覺得很繞,這是因為我們對 物件和從記憶體分析的理解還不透徹,本章后面后面的內容學完再來看一遍,肯定會恍然大悟!
物件的創建*
類與物件的關系
類是一種抽象的資料型別,它是對某一類事物整體描述/定義,但并不能代表某一個具體的事物
-
如動物、植物、手機、電腦
-
Person類、Pet類、Car類等,這些類都是用來描述/定義某一類具體的事物應該具備的特點和行為
物件是抽象概念的具體實體
- 張三就是人的一個具體實體,張三家里的旺財就是狗的一個具體實體
- 能夠體現出特點,展現出功能的是具體的實體,而不是一個抽象的概念
創建與初始化物件
以后我們就不要在每一個類里面都去加上main方法,一個程式只有一個主啟動類
一個專案只存在一個main方法
我們新建一個包,先來一個Student類,再來一個Application類,Application類定義一個main方法就好也是唯一的一個入口,
我們再IDEA上面,把Application類列到一邊,方便我們隨時進行測驗,如圖下

使用new關鍵字創建物件
package com.xiaodi.operator.oop.demo01.demo02;
//學生類
public class Student {
//不管再厲害的人寫的類里面只可能存在兩個東西,1就是屬性 2就是方法
//屬性:可以理解為欄位
String name;
int age;
//方法
public void study() {
//this.代表當前這個類的
System.out.println(this.name+"在學習");
}
}
這樣我們一個簡單的類就定義出來了
package com.xiaodi.operator.oop.demo01.demo02;
//一個專案應該只存在一個main方法
public class Application {
public static void main(String[] args) {
//類是抽象的,我們需要把這個類實體化
//類實體化后會回傳一個自己的物件!
//student物件就是一個Student類的具體實體!
Student student = new Student();
Student xiaoming = new Student();
Student xiaohong = new Student();
//類是抽象的可以這么理解:類就相當一個模板,沒有具體的值
xiaoming.name = "小明";
xiaoming.age = 17;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
System.out.println(xiaohong.name);//默認值null
System.out.println(xiaohong.age);//默認值0
//由此可見,同一個類new過來的物件是互不影響的,是一個具體的實體
}
}
Student類就是一個抽象的模板,然后我們通過new關鍵字,可以創建不一樣的具體的實體!
大家現在再品一下這句話:以類的方式組織代碼,以物件的組織(封裝)資料,是不是恍然大悟!
構造器詳解
使用new關鍵字創建的時候,除了分配記憶體空間之外,還會給 創建好的物件 進行默認的初始化 以及對類中構造器的呼叫
package com.xiaodi.operator.oop.demo01.demo02;
public class Person {
}
package com.xiaodi.operator.oop.demo01.demo02;
//一個專案應該只存在一個main方法
public class Application {
public static void main(String[] args) {
Person person = new Person();
}
}
Person這個類里面我們沒有寫方法,但是還是能new一個實體出來,就證明類里面有一些默認的東西
這個物件是怎么來的,為什么能憑空new出來,我們去看這個Person類生成的class檔案
我們在IDEA點開專案架構,選擇modules,再選擇新增一個目錄(Add Content Root),選擇我們的out目錄,找到對應的class檔案打開就行(如果沒有對應的class檔案,在IDEA運行一下檔案就會生成) Person.class檔案內容如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.xiaodi.operator.oop.demo01.demo02;
public class Person {
public Person() {
}
}
你會發現默認的幫我們加了一個方法,而且這個方法沒有回傳值,方法名字和類名相同;其實這就是一個構造器,
得出結論:一個類即使什么都不寫,它也會存在一個方法
- 類中的構造器也稱為構造方法,是在進行創建物件的時候必須呼叫的,并且構造器有以下兩個特點:
- 1、必須和類的名字相同
- 2、必須沒有回傳型別,也不能寫void
在.java檔案顯示的定義構造器:
package com.xiaodi.operator.oop.demo01.demo02;
public class Person {
public Person() {
}
}
那么有人就會問,這個無參構造器能干什么:
package com.xiaodi.operator.oop.demo01.demo02;
public class Person {
//給我們初始化一些資訊
String name;
//作用
//1、使用new關鍵字,本質是在呼叫構造器
//2、用來初始化值
public Person() {
this.name = "XiaoDi";
}
//有參構造:一旦定義了有參構造,無參構造就必須顯示定義
public Person(String name) {
this.name = name;
}
//快捷鍵:Alt+Insert ,選擇Constructor能自動生成構造器,引數能選
}
package com.xiaodi.operator.oop.demo01.demo02;
//一個專案應該只存在一個main方法
public class Application {
public static void main(String[] args) {
//我們可以多載很多個構造器,使用new實體化一個物件的時候,他會根據你傳的引數來判斷你呼叫的是哪個構造器
Person person = new Person();
Person person1 = new Person("XiaoDi");
System.out.println(person.name);//null
System.out.println(person1.name);//XiaoDi
}
}
構造器總結:
? 構造器定義需注意:
- 和類名相同
- 沒有回傳值
? 構造器的作用:
- 使用new關鍵字的時候構造器就執行了
- 初始化物件的值
? 注意點:
- 定義了有參構造之后,如果想要使用無參構造,就必須顯示的去定義一個無參構造
- 我們可以多載很多個構造器,使用new實體化一個物件的時候,他會根據你傳的引數來判斷你呼叫的是哪個構造器
? 快捷鍵:
- 在IDEA中使用 Alt+Insert ,選擇Constructor能自動生成構造器,引數能選
構造器必須要掌握
類與物件簡單小結
1、類與物件
- 類是一個模板(抽象),物件是一個具體的實體
2、方法
- 定義、呼叫
3、物件的參考
-
參考型別、基本型別(8)
-
物件是通過參考來操作的
4、屬性:也叫欄位(Field)、或成員變數
? 默認初始化:
? 數字:0、0.0;
? char:u0000(轉化為int型別輸出0);
? boolean:false;
? 參考:null
? 屬性的定義:修飾符 屬性型別 屬性名 = 屬性值;
5、物件的創建和使用
- 必須使用new關鍵字創造物件、構造器 :Person xiaodi = new Person();
- 物件的屬性 xiaodi.name;
- 物件的方法 xiaodi.sleep();
6、類
- 類里面只能寫屬性和方法
面向物件三大特性
封裝
- 該露的露,該藏的藏:比如遙控器,暴露一些開關換臺鍵就行,一些底層的就藏起來,用戶不需要全部了解
- 我們程式設計要追求“高內聚,低耦合”,高內聚:就是類的內部資料操作細節自己完成,不允許外部干涉;低耦合:僅暴露少量的方法給外部使用,
- 封裝(資料的隱藏)
- 通常,應禁止直接訪問一個物件中資料的實際表示,而應該通過操作介面來訪問,這稱為資訊隱藏
- 記住這句話就夠了:屬性私有,get\set
聽不懂沒關系上代碼:
package com.xiaodi.operator.oop.demo03;
//學生類
public class Student {
//私有屬性 private:私有
private String name; //名字
private int age; //年齡
private int id; //學號
private char sex; //性別
//提供一些可以操作這個屬性的方法!
//提供一些public 的 get、set方法
//get 獲取這個資料
public String getName() {
return this.name;
}
//set 給這個資料設定值
public void setName(String name) {
this.name = name;
}
//記住Alt+Insert快捷鍵然后選擇Getter 或者Setter 或者Getter and Serter然后能選擇私有屬性進行快速生成
//有人就會問了,封裝有啥子用(可以避免用戶去破壞這個系統,輸入一些注入漏洞代碼 或 輸入一些不符合實際的東西)
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 120 || age < 0) { //不合法
this.age = 3;
}else {
this.age = age;
}
}
}
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo03.Student;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
//當屬性加上private之后就變成私有了
//是不能像原來那樣呼叫的;原來那樣呼叫是因為屬性是public公共的,
//s1.name = "xiaodi"; 這樣是會報紅的
//通過類中公共方法呼叫、和修改
//get 訪問
String name = s1.getName();
//set 修改
s1.setName("曉迪");
//輸出看一下
System.out.println(s1.getName());
//封裝的作用
s1.setAge(999);
System.out.println(s1.getAge());//不符合就表示用戶連基本的嘗試都沒有估計是個兒童,直接定義為3歲即可
}
}
總結->封裝的意義:
- 1、提高程式安全性,保護資料
- 2、隱藏代碼實作細節
- 3、統一介面
- 4、提高了系統的可維護性
作用在什么地方:這就要通過大家平時多寫代碼去積累經驗了,大家現在還只是學會了這樣一個操作,不知道什么地方需要使用到,在我們后面的學習中就會經常去寫封裝這個東西,大家到時候慢慢積累就行,
聊一聊方法多載:比如我們的println,為什么它啥都能輸出,就是因為它多載了很多方法,方法的引數定義的不同,實作不同型別的輸出;大家可以在IDEA中按住Ctrl鍵然后點擊println進去看一下
繼承
-
繼承的本質是對某一批類的抽象,從而實作對現實世界更好的建模,
-
extends的意思是“擴展”,子類是父類的擴展,
-
Java中類只有單繼承,沒有多繼承! (可以理解為一個兒子只能有一個爸爸,一個爸爸能有多個兒子)
-
繼承是類和類之間的一種關系,除此之外,類和類之間的關系還有依賴、組合,聚合等
-
繼承關系的兩個類,一個為子類(派生類),一個為父類(基類),子類繼承父類,使用關鍵字extends來表示
-
子類和父類之間,從意義上講應該具有“is a”的關系,(比如:Wangcai is a dog )
上代碼理解:
Person 人 父類
package com.xiaodi.operator.oop.demo04;
//Person 人 : 父類
public class Person {
//public 公共的,子類能繼承
//protected 受保護的
//default 不寫,默認
//private 私有的,子類不能繼承;一般屬性才會是私有的
public int money = 10_0000; //公共的
private int money1 = 10_0000_0000; //私有的,子類就不能繼承,但是可以通過封裝思想,留一些可以使用這些錢的方法
public void say() {
System.out.println("說話");
}
public int getMoney1() {
return money1;
}
public void setMoney1(int money1) {
this.money1 = money1;
}
}
Student 學生 子類
package com.xiaodi.operator.oop.demo04;
//學生也是人 所以繼承人 :派生類或子類
public class Student extends Person {
//子類可以繼承父類的所有方法、以及屬性;
}
Application 啟動方法(測驗)
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo04.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.money); //父類的屬性 (public)
student.say(); //父類的方法(public)
// System.out.println(student.money1);
// (private)私有的不能繼承,但是可以通過 父類給我留的方法去使用
System.out.println(student.getMoney1());
}
}
繼承小總結:
- 關鍵字:extends;語法:在類名后面加上:[extends 要繼承的類名]
- 子類繼承父類所有方法以及公共屬性
- 私有屬性是不能繼承的(但是一般情況下是會通過封裝的一些方方法去呼叫或修改);
Object類
記住一個快捷鍵Ctrl+H:查看當前繼承的繼承情況
當我們Person類什么都不寫的情況下,new一個Person物件,然后輸入person.就能點出一些方法,但是我們什么都沒定義這是為什么呢?
package com.xiaodi.operator.oop.demo04;
//Person 人
//在Java中,所有的類,都默認直接或者間接繼承Object
public class Person /*extends Object*/{
}
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo04.Person;
public class Application {
public static void main(String[] args) {
Person person = new Person();
//person.(equals(),hashCode(),toString()......)
}
}
- 在Java中,所有的類,都默認直接或者間接繼承Object類(如果是間接繼承,那么Object就是它的爺爺,因為Java中類只有單繼承,沒有多繼承)
super詳解
- this是代表當前類的
- super是代表父類的
? 父類:Person
package com.xiaodi.operator.oop.demo04;
//Person 人
public class Person /*extends Object*/{
//修飾符 protected 受保護的
protected String name = "曉迪";
public void print() {
System.out.println("Person");
}
}
? 子類:Student
package com.xiaodi.operator.oop.demo04;
//學生也是人 所以繼承人 :派生類或子類
public class Student extends Person {
private String name = "小迪";
public void print() {
System.out.println("Student");
}
public void text(String name) {
System.out.println(name);//方法里的name
System.out.println(this.name);//當前類的name
System.out.println(super.name);//父類的name
}
public void text1() {
print();//這個是當前類的print
this.print();//這個也是當前類的print(建議寫法)
super.print();//父類里的print
}
}
? 啟動程式(測驗):Application
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo04.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.text("XiaoDi");
System.out.println();//換行符
student.text1();
}
}
輸出結果:
? XiaoDi
? 小迪
? 曉迪
? Student
? Student
? Person
想一下如果new子類的時候構造器的執行順序是怎么樣的:
? 父類:Person
package com.xiaodi.operator.oop.demo04;
//Person 人
public class Person /*extends Object*/{
public Person() {
System.out.println("Person無參構造執行了");
}
}
? 子類:Student
package com.xiaodi.operator.oop.demo04;
//學生也是人 所以繼承人 :派生類或子類
public class Student extends Person {
public Student() {
super();//子類構造器隱藏代碼,在構造器的第一行,這一行不寫效果也是一樣的
//this("xiaodi"); 我們要呼叫自己的有參構造this("xiaodi");,也需要放在第一行,否則報錯,也就是說兩者在同一個構造器里只能使用一個;
System.out.println("Student無參構造執行了");
}
public String name;
public Student(String name) {
this.name = name;
}
}
? 啟動程式(測驗):Application
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo04.Student;
public class Application {
public static void main(String[] args) {
Student student = new Student();
}
}
輸出:
? Person無參構造執行了
? Student無參構造執行了
也就是說父類的無參構造器先執行了
- 如果說我們要呼叫自己的有參構造this("xiaodi");,也需要放在第一行,否則報錯,也就是說兩者在同一個構造器里只能使用一個;這個就是super關鍵字坑爹的地方
那么假如我的父類里面沒有無參構造器:
? 父類:Person
package com.xiaodi.operator.oop.demo04;
//Person 人
public class Person /*extends Object*/{
public String name;
public Person(String name) {
this.name = name;
}
}
? 子類:Student
package com.xiaodi.operator.oop.demo04;
//學生也是人 所以繼承人 :派生類或子類
public class Student extends Person {
public Student() {//報錯
System.out.println("Student無參構造執行了");
}
}
- 會直接導致子類的無參構造也寫不了
但是也不是沒有辦法,沒有無參構造,那你顯示的寫出呼叫父類的有參構造就行了:
? 子類:Student
package com.xiaodi.operator.oop.demo04;
//學生也是人 所以繼承人 :派生類或子類
public class Student extends Person {
public Student() {
super("小迪");
System.out.println("Student無參構造執行了");
}
}
super小總結:
- 1、super是在子類中呼叫父類的屬性或者方法 例如:super.name; super.print();(只能出現在子類的方法或構造方法中)
- 2、super呼叫父類的構造方法,必須在子類的構造方法的第一個(是隱藏代碼,寫不寫效果是一樣的)
- super呼叫父類構造 和 this呼叫本類(子類)有參構造 不能同時在一個構造方法里面使用(也就是說能通過騷操作來解決):
? 父類:Person
package com.xiaodi.operator.oop.demo04;
//Person 人
public class Person /*extends Object*/{
public Person() {
System.out.println("Person無參構造執行了");
}
public String name;
}
? 子類:Student
package com.xiaodi.operator.oop.demo04;
//學生也是人 所以繼承人 :派生類或子類
public class Student extends Person {
public Student() {//報錯
this(18);
System.out.println("Student無參構造執行了");
}
public int age;
public Student(int age) {
super();
this.age = age;
}
}
? 如果要呼叫子類中的有參構造方法的話,且不影響代碼,就能像上面這種方法寫:呼叫子類有參構造的同時還呼叫了父類的無參構造,以上代碼的執行順序是:1父類無參構造、2age=18、3子類無參構造
super VS this
- 代表的物件不同
- this:本身呼叫者這個物件
- super:代表父類物件的應用
- 前提:
- this:沒有繼承也可以使用
- super:只能在繼承的條件下才能使用
- 構造方法:
- this():本類的構造
- super():父類的構造
方法重寫
靜態方法
? 父類:B
package com.xiaodi.operator.oop.demo04;
//重寫都是方法重寫,和屬性無關
public class B {
public static void test() {
System.out.println("B->test()");
}
}
? 子類:A
package com.xiaodi.operator.oop.demo04;
public class A extends B{
public static void test() {
System.out.println("A->test()");
}
}
? 啟動程式:Application
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo04.A;
import com.xiaodi.operator.oop.demo04.B;
public class Application {
public static void main(String[] args) {
A.test();
B.test();
}
}
輸出:
? A->test()
? B->test()
非靜態方法
? 父類:B
package com.xiaodi.operator.oop.demo04;
//重寫都是方法重寫,和屬性無關
public class B {
public void test() {
System.out.println("B->test()");
}
}
? 子類:A
package com.xiaodi.operator.oop.demo04;
public class A extends B{
//這里使用Alt+Insert快捷鍵生成重寫選擇 有Override的
//Override:重寫
@Override //注解:有功能的注釋!這個不用糾結我們后面會單獨講
public void test() {
System.out.println("A->test()");
}
}
啟動程式:Application
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo04.A;
import com.xiaodi.operator.oop.demo04.B;
public class Application {
//靜態方法和非靜態方法區別很大!
//靜態方法:方法的呼叫只和左邊定義的資料型別有關(這個不叫重寫)
//非靜態方法:非靜態(且修飾符只能是public):子類重寫了父類的方法(這個才叫重寫)
public static void main(String[] args) {
//
A a = new A();
a.test(); //A
//父類的參考指向了子類
B b = new A();//子類重寫了父類的方法
b.test();//B
}
}
方法重寫小總結:
- 首先需要有繼承關系,子類重寫父類的非靜態方法!
特點:
- 1、方法名必須相同
- 2、引數串列必須相同 (多載是當前類的,而且引數串列不相同)
- 3、修飾符:重寫時子類的訪問控制修飾符不能比父類的范圍小(public>protected>default>private)(這個先不要糾結,隨著后面的學習再來看這個筆記)
- 拋出的例外;范圍可以被縮小,但不能擴大;(后面再去了解)ClassNotFoundException -->Exception(大)
重寫,子類的方法和父類必須要一致;方法體不同!
為什么要重寫:
- 1、父類的功能,子類不一定需要,或者不一定滿足!
快捷鍵:Alt+Insert,然后選擇override
多型
多型
-
即同一方法可以根據發送物件的不同而采用多種不同的行為方式
-
一個物件的實際型別是確定的,但可以指向物件的參考的型別有很多
-
多型存在的條件
- 有繼承關系
- 子類重寫父類的方法
- 父類參考指向子類物件
-
注意:多型是方法的多型,屬性沒有多型性
上代碼:
? 父類:Person
package com.xiaodi.operator.oop.demo05;
public class Person {
public void run() {
System.out.println("run");
}
}
? 子類:Student
package com.xiaodi.operator.oop.demo05;
public class Student extends Person{
@Override
public void run() {
System.out.println("son");
}
public void eat() {
System.out.println("eat");
}
}
? 啟動程式:Application
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo05.Person;
import com.xiaodi.operator.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
//一個物件的實際型別是確定的
//new Student();
//new Person();
//可以指向的參考型別就不確定了
Student s1 = new Student();
Person s2 = new Student();//父類的參考指向子類
Object s3 = new Student();
//得出結論:一個類的實際物件型別是確定的,但是指向的參考型別可以是他的父類和Object類
//我們現在在Person類里寫一個run方法,我們就能用s2去呼叫(輸出run);然后我們去子類重寫一下這個rum方法
s2.run();
s1.run();
//兩個都輸出son(也就是說如果子類重寫了父類的方法,那么就執行子類的方法)
//我們在子類加了一個eat方法(父類沒有):那么s2還能去呼叫這個方法嗎(s1肯定可以,因為s1就是Student型別)
s1.eat();
//s2.eat(); 是不可以這樣去調的(因為我們的s2是Person型別的,person里沒有這個方法)
//但是如果person里面和student里面都有的話,子類沒有重寫的情況下那么它就呼叫父類的,如果子類重寫了那么就呼叫子類的
//得出結論:物件能執行哪些方法,主要看物件左邊的型別,和右邊關系不大
// Student(子類)型別能呼叫的方法都是自己的或者繼承父類的
// Person(父類)型別,雖然可以指向子類,但是不能呼叫子類獨有的方法
}
}
多型小總結:注意事項(一定要把注意事項搞明白我們才能避免錯誤的發生)
- 1、多型是方法的多型,屬性沒有多型
- 2、必須是有父子關系 (如果沒有關系轉換的話會報一個例外:ClassCastException!)
- 3、多型存在條件:(1)需要有繼承關系 (2)方法需要重寫 (3)父型別參考指向子類物件
- 有些方法不能重寫:
- 1、static 方法是屬于類的,它不屬于實體
- 2、final 常量
- 3、private方法是私有的也不能重寫
- 有些方法不能重寫:
instanceof和 型別轉換(參考型別)
instanceof
instanceof:判斷一個物件是什么型別(有父子關系就ok,沒有就不行)
演示:
? 父類:Person
package com.xiaodi.operator.oop.demo05;
public class Person {
public void run() {
System.out.println("run");
}
}
? 子類:Student
package com.xiaodi.operator.oop.demo05;
public class Student extends Person{}
? 子類:Teacher
package com.xiaodi.operator.oop.demo05;
public class Teacher extends Person {}
? 啟動程式:Application
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo05.Person;
import com.xiaodi.operator.oop.demo05.Student;
import com.xiaodi.operator.oop.demo05.Teacher;
public class Application {
public static void main(String[] args) {
//當前的關系:Opject > Person > Student
//當前的關系:Opject > Person > Teacher
Object object = new Student();
System.out.println(object instanceof Student);//ture
System.out.println(object instanceof Person);//ture
System.out.println(object instanceof Object);//ture
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//false
System.out.println("=====================");
Person person = new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Teacher);//false
// System.out.println(person instanceof String);//編譯報錯!
System.out.println("=====================");
Student student = new Student();
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Person);//true
System.out.println(student instanceof Object);//true
// System.out.println(student instanceof Teacher);//編譯報錯!
// System.out.println(student instanceof String);//編譯報錯!
}
}
得出結論:System.out.println(x instanceof y);
- 能不能編譯通過主要看x的參考型別跟y是否存在父子關系(如果存在編譯通過,如果不存在編譯不通過)
- 而結果主要看x指向的實際型別是不是y的子型別(就是說x指向的型別跟y有關系就為true)
型別轉換
? 父類:Person
package com.xiaodi.operator.oop.demo05;
public class Person {
public void run() {
System.out.println("run");
}
}
? 子類:Student
package com.xiaodi.operator.oop.demo05;
public class Student extends Person{
public void go() {
System.out.println("go");
}
}
? 啟動程式:Application
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo05.Person;
import com.xiaodi.operator.oop.demo05.Student;
public class Application {
public static void main(String[] args) {
//現在我們知道Student里面能用兩個方法,一個是go,一個是繼承過來的run
//型別之間的轉換 :父(高) 子(低)
//低轉高 (子類轉換為父類:可能丟失自己本來的一些方法!)
Student student = new Student();
Person ps = student;
ps.run();
Person person = new Student();
//我們這邊person.go();是會報錯的,如果我們想讓他執行就要進行型別轉換
//高轉低(強制轉換)Person型別轉換成Student型別
Student person1 = (Student) person;
person1.go();
//和成一句代碼就是象下面這樣
((Student) person).go();
}
}
多型總結:
- 1、父類參考指向子類的物件
- 2、把子類轉換為父類,向上轉向
- 3、把父類轉換為子類,向下轉型;強制轉換
- 4、方便方法的呼叫,減少重復的代碼!使代碼變簡潔!
這些大家可能會很懵,都是一些理論知識,每個人的理解能力不一樣,知道怎么用就行,等后面用到了,你自然就懂了,后面自己慢慢悟,別著急!別著急!別著急!
static關鍵字詳解
static:加在屬性上叫靜態屬性,加在方法上叫靜態屬性
package com.xiaodi.operator.oop.demo06;
public class Student {
private static int age; //靜態屬性
private double score; //非靜態屬性
public void run() {} //非靜態方法
public static void go() {} //靜態方法
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);//通過類呼叫
System.out.println(age);//跟上面的效果一樣
//System.out.println(score);//非靜態屬性會報錯
System.out.println(s1.age);//通過物件呼叫
System.out.println(s1.score);//通過物件呼叫
Student.go();
go();
s1.go();
s1.run();
}
}
? 一個類里面的靜態屬性或方法在記憶體中是和類一起加載的,能通過類呼叫(在本類中甚至能直接寫);非靜態的則需要通過new
代碼塊(補充)
package com.xiaodi.operator.oop.demo06;
public class Person {
//匿名代碼塊
{
System.out.println("匿名代碼塊");
}
//靜態代碼塊
static {
System.out.println("靜態代碼塊");
}
//構造器
public Person() {
System.out.println("構造方法");
}
public static void main(String[] args) {
Person person1 = new Person();
System.out.println();
Person person2 = new Person();
}
}
執行結果:
? 靜態代碼塊
? 匿名代碼塊
? 構造方法
? 匿名代碼塊
? 構造方法
通過執行結果我們發現:靜態代碼塊最優先,其次才是匿名代碼塊,最后才執行構造方法,且靜態代碼塊只執行一次
匿名代碼塊我們一般用來賦一些初始值
靜態匯入包(一般人很少這么玩,知道有這個東西就行)
package com.xiaodi.operator.oop.demo06;
//import java.lang.Math.random; 匯入java.lang下面的Math類下面的random方法,這樣是會報錯的,需要加上靜態
import static java.lang.Math.random;
public class Test {
public static void main(String[] args) {
//生成亂數:Math.random
System.out.println(Math.random()); //這樣呼叫是不是很麻煩,我們把方法導進來
System.out.println(random());//導進來之后就能這樣寫
}
}
主要說一下:通過final修飾的類就不能被繼承了(意思就是斷子絕孫了哈哈哈)
抽象類和介面
抽象類
-
abstract修飾符可以用來修飾方法也可以用來修飾類,如果修飾方法,那么該方法就是抽象方法;如果修飾類,那么該類就是抽象類
-
抽象類中可以沒有抽象方法,但是有抽象方法的類一定要宣告為抽象類
-
抽象類,不能使用new關鍵字來創建物件,它是用來讓子類繼承的,
-
抽象方法,只有方法的宣告,沒有方法的實作,它是用來讓子類實作的,
-
子類繼承抽象類,那么就必須要實作抽象類沒有實作的抽象方法,否則該子類也要宣告為抽象類
? 父類:Action
package com.xiaodi.operator.oop.demo07;
//abstract 抽象類
public abstract class Action {
//abstract 抽象方法,只有方法的名字,沒有方法的實作 (約束,讓別人幫我們實作)
public abstract void doSomething();
//抽象類的特點:
//不能new這個抽象類,只能靠子類去實作它:約束!
//只能去new它的子類,如果它的子類沒實作,只能去new它的子子類
//抽象類里面可以寫普通方法
//抽象方法必須在抽象類中
//思考?
// 抽象類既然不能new,那么存在構造器嗎?(大家自己打開class編譯看一下)
// 抽象類存在的意義? (提高開發效率)
}
? 子類:A
package com.xiaodi.operator.oop.demo07;
//抽象類的所有方法,繼承它的子類,都必須要重寫去實作他的方法,除非它的子類也是抽象類
//抽象類extends:單繼承,有局限性,(介面可以多繼承)
public class A extends Action{
@Override
public void doSomething() {
System.out.println("doSomething");
}
}
抽象類并不是我們的重點,了解一下就好了;下面的介面跟抽象類非常像用的人也比較多(介面可多繼承)
介面
-
普通類:只有具體實作
-
抽象類:具體實作和規范(抽象方法)都有! 業余約束!
-
介面:只有規范!自己無法寫方法~專業的約束! 能干約束和實作分離:面向介面編程
-
介面就是規范,定義的是一組規則,體現了現實世界中“如果你是...則必須能...”的思想,如果你是天使,則必須能飛,如果你是汽車,則必須能跑,如果你是好人,則必須干掉壞人;如果你是壞人,則必須欺負好人,
-
介面本質是契約,就像我們人間的法律一樣,制定好后大家都遵守,
-
OO的精髓,是對物件的抽象,最能體現這一點的就是介面,為什么我們討論設計 模式都只針對具備了抽象能力的語言(比如c++、java、c#等),就是因為設計模式所研究的,實際上就是如何合理的去抽象,
宣告類的關鍵字是class,宣告介面的關鍵字是interface
? 介面:UserService
package com.xiaodi.operator.oop.demo08;
//interface 定義的關鍵字 介面都需要有實作類
public interface UserService {
//介面中的所有定義其實都是抽象的
//屬性默認的是一個靜態常量 public static final
int age = 99; //一般不會這么玩
// public abstract void add(String name);//假設方法修飾符你不寫默認就是public abstract
//所以介面都可以像下面這樣去寫 回傳值型別 方法的名字([引數可選]);
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
? 介面:TimeService
package com.xiaodi.operator.oop.demo08;
public interface TimeService {
void timer();
}
? 介面的實作:UserServiceImpl
package com.xiaodi.operator.oop.demo08;
//實作類名一般加一個Impl就行
//implements 介面實作類關鍵字 (從側面實作了我們的多繼承)
public class UserServiceImpl implements UserService,TimeService{
//如果你要去實作介面里面的所有定義,你必須要去重寫里面的所有方法
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
介面的點作用:
- 1、約束
- 2、定義一些方法,讓不同的人實作(比如你有10個員工,但是他們都會去完成一份共同的作業,10個人實作的是一個介面,但是有10種不同是實作方式)
- 3、方法都是:public abstract 屬性都是:public static final,一般沒人會去玩屬性
- 4、介面不能被實體化~介面中沒有構造方法
- 5、可以通過implements實作多個介面
- 6、實作介面必須重寫介面里面的方法
? 抽象的思想很難鍛煉 :Java誰都會對吧 那么為什么有些人能做到架構師有些人不能 (架構師就需要抽象思維非常非常好,你要把一個系統的結構全部抽象成介面,你能通過介面去定義一個系統的時候,那時你的架構能力就已經很強了,但是大部分人做不到)
內部類
-
內部類就是在一個類的內部在定義一個類,比如,A類中定義了一個B類,那么B類相對A類來說就稱為內部類,而A類相對B類來說就是外部類,
-
1、成員內部類
package com.xiaodi.operator.oop.demo09;
public class Outer {
private int id = 10;
public void out() {
System.out.println("這是外部類的方法");
}
public class Inner{
public void in() {
System.out.println("這是內部類的方法");
}
//內部類訪問外部類的私有屬性
public void getId() {
System.out.println(id);
}
}
}
測驗:Application
package com.xiaodi.operator.oop;
import com.xiaodi.operator.oop.demo09.Outer;
public class Application {
public static void main(String[] args) {
//外部類的實體化大家都懂了吧
Outer outer = new Outer();
//這個內部類要通過外部類來實體化
Outer.Inner inner = outer.new Inner(); //是不是非常奇葩
inner.in();
inner.getId();
}
}
- 2、靜態內部類
package com.xiaodi.operator.oop.demo09;
public class Outer {
private int id = 10;
public void out() {
System.out.println("這是外部類的方法");
}
public static class Inner{
public void in() {
System.out.println("這是內部類的方法");
}
//外部的id就拿不到了,因為是靜態的類存在的時候就存在,那個時候id這個屬性還沒出生
}
}
- 3、區域內部類
package com.xiaodi.operator.oop.demo09;
public class Outer {
//區域內部類 寫在方法里面的
public void method() {
class Inner {
public void in() {
}
}
}
}
- 4、匿名內部類
package com.xiaodi.operator.oop.demo09;
public class Outer {
public static void main(String[] args) {
//我們正常實體化是像下面這樣干的
Apple apple = new Apple();
//匿名內部類就是沒有名字去初始化類,不用把實體保存到變數中
new Apple().eat();
//匿名內部類 介面的實作
new UserService() {
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat() {
System.out.println("1");
}
}
interface UserService {
void hello();
}
- 5、也叫內部類
package com.xiaodi.operator.oop.demo09;
public class Outer {
}
//一個java檔案中可以有多個class類,但是只能有一個public class類
class A {
}
大家這里先了解一下就可以,講內部類主要目的是讓大家知道有這個東西,以后分析原始碼看到,不要不知道這是什么東西就行
本章內容較多,建議看兩遍以上,第二遍你會發現一些第一遍不理解的,慢慢理解了
本章內容較多,建議看兩遍以上,第二遍你會發現一些第一遍不理解的,慢慢理解了
本章內容較多,建議看兩遍以上,第二遍你會發現一些第一遍不理解的,慢慢理解了
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/447038.html
標籤:其他
