面向對象
面向對象編程(Object-Oriented Programming, OOP)
初識面向對象
面向對象編程的本質就是:以類的方式組織代碼,以對象的形式組織封裝數據。
抽象
從認識論角度考慮是現有對象後有類。對象,是具體的事物。類,是抽象的,是對對象的抽象
從代碼運行角度考慮是先有類後有對象。類是對象的模版
三大特性:
封裝
繼承
多態
方法回顧和加深
修飾符
返回值類型
可以爲八大基本類型和引用類型,也可以爲void(無返回值),返回值類型爲void時,書寫return;
return 與 break區別:
break:跳出switch,結束循環
return:結束方法
參數列表:參數類型+參數名 以及可變長參數
拋出異常:
public void readFile(String file) throws IOException {
}
方法的調用
主要分爲有static修飾和無static修飾
當有static修飾符修飾的方法,是跟類一起加載的,即使類沒有被實例化也可以直接調用。類名.方法名 調用該方法
當無static修飾符修飾的方法,是類實例話後才加載的,不可以直接調用,需要實例化類後通過類的實例化對象進行調用
//學生類 public void say(){ System.out.println("學生說話了"); } //另一個demo類 public static void main(String[] args) { //對象類型 對象名 = 對象值 student student = new student(); student.say(); }
值傳遞和引用傳遞
先來看值傳遞
//值傳遞
public class Demo04 {
public static void main(String[] args) {
int a = 1;
int b = 2;
System.out.println(a);
System.out.println(b);
System.out.println("===================change===================");
Demo04.change1(a);
System.out.println(a); //1,因爲沒有把change方法中的值返回出來,所以並沒有發生變化
b = Demo04.change2(b);
System.out.println(b);
}
//返回值爲空
public static void change1(int a){
a = 10;
}
public static int change2(int b){
b = 20;
return b;
}
引用傳遞
//引用傳遞:一般是傳遞一個對象,本質還是值傳遞
//一個class裏只能有一個public class,但可以有多個class
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name); //null
//這裏不同於值傳遞,因爲在change方法中傳入的是person這個對象,修改的是person這個對象的.name屬性,所以成功了,
Demo05.change(person);
System.out.println(person.name);
}
public static void change(Person person){
person.name = "Zh1z3ven";
}
}
//定義了一個person類,有一個屬性:name
class Person{
String name; //null
}
this關鍵字
代表當前這個類或者對象
對象的創建分析
使用new關鍵字創建對象
在使用new關鍵字創建的時候,除了分配內存空間之外,還會給創建好的對象進行默認的初始化以及對類中構造器的調用。
類中的構造器也稱爲構造方法,是在進行創建對象的時候必須調用的。並且構造器有以下兩個特點
1、必須和類的名字相同
2、必須沒有返回類型,也不能寫void
public class Student {
//屬性:字段
String name; //創建實例化對象時默認爲null
int age; //0
//方法
public void study(){
System.out.println(this.name + "is Learning...");
}
}
public class Application {
public static void main(String[] args) {
//類是抽象的,需要實例化
//類實例化後會返回一個自己的對象
//stduent就是student類的具體實例
Student xiaoming = new Student();
Student agou = new Student();
xiaoming.name = "小明";
xiaoming.age = 18;
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
agou.name = "阿狗";
agou.age = 18;
System.out.println(agou.name);
System.out.println(agou.age);
}
}
構造方法
1、和類名相同
2、沒有返回值
3、new的本質是在調用構造方法
4、初始化對象的值
5、定義有參構造之後,如果想使用無參構造,需要顯示的定義一個無參的構造,不然就只有有參構造方法
6、alt + insert 快捷鍵
public class Person {
//一個類即使什麼都寫,他也會存在一個構造方法
//構造方法用來初始化值
/* 形如":
public Person(){
}
*/
//顯示定義構造方法
String name;
//使用new關鍵字必須有構造方法,本質是在調用構造方法
//無參構造方法
public Person(){
//可以幫助我們初始化對象的屬性值
this.name = "Zh1z3ven";
}
//有參數構造:一旦定義了有參構造,無參構造方法必須顯示定義
//this.name代表Person類的屬性name
//後面的name爲有參構造方法傳遞的String name參數
//alt+insert生成構造方法
public Person(String name){
this.name = name;
}
public static void main(String[] args) {
//new 實例化了一個對象
Person person = new Person();
System.out.println(person.name);
}
}
創建對象內存分析
代碼:
public class Pet {
public String name;
public int age;
//無參構造
public void shout(){
System.out.println("叫了一聲");
}
}
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺財";
dog.age = 3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
}
}
首先會現在方法區加載Application類(類中包括其main()方法,常量池(存放字符串;但int型數字不算在常量池內)),並在棧中加載main方法
main方法在棧的最低下,方法的調用執行都在棧中執行。之後運行到Pet dog = new Pet()
時,先會在方法區實例化對象所需的模版,即加載一個Pet類,包括該類的屬性、常量池和非靜態方法。之後當加載完後 講賦值語句右邊的new Pet()
賦值給左邊的Pet dog
時,在棧中會加載一個dog
的變量,dog
變量暫時只是一個引用變量,引用在堆中生成的實例化對象,類似於指針,該引用指向該實例化對象在堆中的內存地址。
而在實例化對象內的shout()
是在引用方法區中Pet
類中的shout()
方法。之後執行到賦值語句時也就是
dog.name = "旺財";dog.age = 3;
這時會把Application
類中的常量池中的旺財
賦值給堆
中的name
,之後3
賦值給堆中的age
,同時shout()
調用的是Pet
類中的shout()
方法(因爲並沒有參數傳遞,所以是同一個方法)
在方法區中的靜態方法區內,這裏存放的是被static
修飾符修飾的方法都會同類同時被加載,所以可以不用通過實例化對象就可以直接調用被static
修飾的方法。
簡單小結類與對象
類是一個模版,是實例化對象的模版
對象是一個具體的實例,是一個類的實例化對象
方法的定義
修飾符 返回值類型 方法名(參數類型 參數名){
方法體
...
return 返回值;
}
方法的調用
調用方法:對象名.方法名(實參列表)
Java支持兩種調用方法的方式,根據方法是否有返回值來選擇。
return不僅可以返回值,同樣可以結束這個方法。例:return 0;結束當前方法。
當方法返回一個值的時候,方法調用通常被當作一個值賦值給一個變量。
int larger = max(30, 40);
當方法返回值爲void時,方法調用一定是一條語句
System.out.println("Hello, Zh1z3ven!");
對象的引用
引用類型:
基本類型8個(byte、short、int、long、float、double、boolean、char)
剩下的都是引用類型,對象也是通過引用來操作的:棧-->堆(地址)
屬性:字段field 成員變量,默認初始化(數字:0,char:u000,boolean:false,引用:null)
屬性的定義:
修飾符 屬性類型 屬性名 = 屬性值
對象的創建和使用
必須使用new關鍵字創建對象 ,注意構造方法,Person zh1z3ven = new Person()
對象的屬性: zh1z3ven.name
對象的方法: zh1z3ven.sleep()
類
靜態的屬性:屬性
動態的行爲:方法
面向對象三大特性
封裝
程序設計追求“高內聚、低耦合”,高內聚就是類的內部數據操作細節自己完成,不允許外部干涉;低耦合:僅暴露少量的方法給外部使用。
屬性私有 get/set
屬性私有,即用private修飾屬性
get/set,對外提供get set方法使得外部可通過該方法控制屬性值
同時可在set方法中進行安全設置,提高程序的安全性
/*1、提高程序的安全性2、隱藏代碼的實現細節3、統一接口 get、set形成規範4、提高系統可維護性 */public class Student { //屬性私有,加private關鍵字去修飾屬性 private String name; private int id; private char sex; private int age; //提供一些可以操作這個屬性的方法 //提供public的get或set方法 public String getName(){ return this.name; } public void setName(String name){ this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { if (age > 120 || age < 0){ this.age = 3; }else { this.age = age; } }}public class Application { public static void main(String[] args) { Student s1 = new Student(); s1.setName("Zh1z3ven"); System.out.println(s1.getName()); //並不能通過形似s1.name直接訪問操作類的屬性,但是可以通過某些可被調用的外部方法去操作這個類的私有屬性值 //封裝:1、屬性私有。2、提供可被外部調用的方法去操作私有屬性 //alt+insert 選中getter and setter一鍵生成 get set方法, //mac爲control+return,選中所需要設置的屬性即可直接生成 //或者右鍵選擇generate }}
繼承
extends
繼承的本質是對某一批類的抽象,從而實現對現實世界的更好的建模
Java中只有單繼承沒有多繼承
繼承就是擴展的意思,子類是對父類的擴展。
子類可以繼承父類的所有方法
繼承是類和類之間的一種關係,除此之外還有依賴,組合,聚合等
繼承關係的倆個類,一個爲子類(派生類)一個爲父類(基類)。子類繼承父類,使用關鍵字extends表示
子類和父類之間,從意義上講因該具有 is a 的關係
Ctrl + h 可以看該類的繼承關係
object類
在Java中所有的類都默認直接或間接繼承於Object
super
如果父類重寫了有參構造,最好也寫上無參構造,以便後續子類去繼承該父類(不然只能調用父類的有參構造方法,默認是調用無參的構造方法)
- super調用父類的構造方法必須在構造方法的第一個
- super必須只能出現在子類的方法或構造方法中
- super和this不能同時調用構造方法
this代表本身調用者的這個對象
super代表父類對象的引用
super必須存在繼承關係纔可以使用
this(); 代表本類的構造方法
super(); 代表父類的構造方法
//父類public class Person { public Person(){ System.out.println("Person無參執行了"); } public int money = 1000000000; public void say(){ System.out.println("說了一句話"); } protected String name = "Zh1z3ven"; public void print(){ System.out.println("Person"); }}
//子類public class Student extends Person { //默認調用了父類的無參構造,調用父類的構造方法必須在子類構造方法的第一行 public Student(){ super(); //也可以不寫 System.out.println("Student無參執行了"); } private String name = "zh1z3ven"; public void print(){ System.out.println("Student"); } public void test1(){ print(); this.print(); super.print(); } public void test(String name){ System.out.println(name);//傳遞的參數name System.out.println(this.name);//student的屬性name System.out.println(super.name);//父類的屬性name }}
public class Application { public static void main(String[] args) { Student student = new Student(); student.test("zz"); student.test1(); }}
方法重寫
重寫都是非靜態方法的重寫(static方法屬於類的方法,不屬於實例的方法,重寫指的是重寫實例的方法)
final修飾的是常量池中的常量,不能被重寫。private方法不能被重寫。
子類重寫方法的權限修飾符要大於父類 public>protected>default>private
方法名必須相同、參數列表必須相同(不然就是重載了)
拋出異常的範圍與權限修飾符的範圍一致
子類的方法與父類的方法必須要一致,但是方法體不同
爲什麼需要重寫?
1.父類的功能,子類不一定需要,或者不一定滿足
- alt+insert / control + return
@override
@Override //註解:有功能的註釋 public void test() { super.test(); //默認調用父類的方法,也可以自己重寫 }
當重寫一個非靜態父類方法時,在子類中contorl + return即可生成一個重寫方法
靜態方法是屬於類的方法,當下面
test()
方法爲static
修飾的靜態方法時,那麼會各自調用自己類中的test()
方法。因爲此時調用的是類中的靜態方法;而當沒有static
修飾時,這時調用的都是A
類中的test()
方法,因爲這時調用的是A
的實例化對象加載的test()
方法(只有static
修飾的方法纔會和類一起在方法區中同時加載)可以簡單理解爲一旦子類重寫了父類的方法,就執行子類的方法
class A extends BA a = new A();a.test();//父類引用指向子類B b = new A();b.test()A類中test()實現:System.out.println("A=>test()");B類中test()實現:System.out.println("B=>test()");
多態
class Student extends PersonStudent s1 = new Student();Person s2 = new Student();Object s3 = new Student();
一個對象的實際類型是確定的(
new Student();
),但是可以指向的引用類型就不確定了(Student s1
/Person s2
/Object s3
)多態注意事項:
- 多態是方法的多態,屬性沒有多態
- 父類和子類有聯繫.
ClassCastException
類型轉換異常- 多態存在的條件:繼承關係、方法需要重寫、父類的引用指向子類對象
Father f1 = new son();
Instanceof
判斷一個對象是什麼類型,可以判斷兩個類之間是否存在繼承關係
//Object > String //Object > Person > Student //Object > Person > Teacher Object object = new Student(); //Object類型 System.out.println(object instanceof Student); //true System.out.println(object instanceof Person); //true System.out.println(object instanceof Object); //true 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 Object); //true System.out.println(person instanceof Person); //true System.out.println(person instanceof Student); //true System.out.println(person instanceof Teacher); //false //System.out.println(person instanceof String); //編譯報錯 }
類型轉換
public static void main(String[] args) { //類型之間的轉化: 父 子 //低 --> 高 可以自動轉換 //高 --> 低 需要強制轉換 Person obj = new Student(); //student將這個對象轉換爲Student類型,就可以使用Studen類型的方法 Student student = (Student) obj; //強制轉換 student.go(); ((Student) obj).go(); //強制轉換 Person person = student; //自動轉化 }
static關鍵字
static : 修飾成員變量或者方法,修飾爲靜態變量或者靜態方法
被static修飾的屬於類,非static的屬於對象
靜態變量屬於類的變量,存儲在方法區的靜態方法區內,是一塊固定的內存,隨着類的加載同時加載,當直接使用類去調用一個變量說明此變量就是靜態變量
非靜態方法也可以直接調用靜態方法
靜態方法可以直接調用,無需實例化對象
//static : 修飾成員變量或者方法,修飾爲靜態變量或者靜態方法//被static修飾的屬於類,非static的屬於對象public class Student { //靜態變量屬於類的變量,存儲在方法區的靜態方法區內,是一塊固定的內存,隨着類的加載同時加載,當直接使用類去調用一個變量說明此變量就是靜態變量 private static int age; //靜態變量 private double score; //非靜態變量 public void run(){ go(); //非靜態方法也可以直接調用靜態方法 } public static void go(){ } public static void main(String[] args) { Student s1 = new Student(); System.out.println(Student.age); System.out.println(s1.age); System.out.println(s1.score); s1.run(); Student.go(); //靜態方法可以直接調用,無需實例化對象 go(); }}
public static void main(String[] args) { Student student = new Student(); System.out.println("=================="); Student student1 = new Student(); } // 2 :賦初始值 { System.out.println("匿名代碼塊"); //匿名代碼塊 //隨着對象的創建而創建,在構造方法之前 } // 1 : 只執行一次,後面不會執行 static { //靜態代碼快,方便加載初始化一些數據 //隨着類的加載而加載,永久只執行一次 System.out.println("靜態代碼快"); } // 3 //alt+inset/contorl+return --> select none public Student() { System.out.println("構造方法"); }}輸出結果:靜態代碼快匿名代碼塊構造方法==================匿名代碼塊構造方法
利用static靜態導入包及常量
//靜態導入包import static java.lang.Math.random;import static java.lang.Math.PI;
抽象類和接口
抽象類
abstract
修飾符可以修飾方法也可以修飾類,如果修飾方法,那麼該方法就是抽象方法;如果修飾類,該類就是抽象類
抽象類中可以沒有抽象方法,但是抽象方法的類一定要聲明爲抽象類
抽象類,不能使用new關鍵字來創建對象,它是用來讓子類繼承的(類是單繼承)
抽象方法,只有方法的聲明,沒有方法的實現,它是用來讓子類方法去實現的
子類繼承抽象類,那麼就必須要實現抽象類沒有實現的抽象方法,否則該子類也要聲明爲抽象類
public abstract class Action { //抽象方法,只有方法名字,沒有方法的實現 public abstract void doSomething(); //抽象類不能new,只能靠子類去實現 //抽象類可以寫普通方法 //抽象類存在構造方法嗎? //抽象類存在的意義?提高開發的可擴展性}//繼承抽象類的子類必須重寫抽象方法並實現,除非子類也是abstractpublic class A extends Action{ @Override public void doSomething() { }}
接口
聲明接口的關鍵字是interface
接口可以多繼承
普通類:只有具體的實現
抽象類:具體實現和規範(抽象方法)都有
接口:只有規範。約束和實現分離(面向接口編程)
接口就是規範,定義的是一組規則,體現了現實世界中“如果你是。。。你必須能。。”的思想。如果你是天使你必須能飛,如果你是汽車,你必須能跑。
接口的本質是契約,就像我們人間的法律一樣。制定好後大家都遵守。
OO的精髓,是對對象的抽象,最能體現這一點的就是接口。
//interface 定義的關鍵字public interface UserService { //接口中的所有定義其實都是抽象的,不能在接口中實現方法 //修飾符默認爲 public abstract //常量。默認修飾符爲 public static final int AGE = 99; void add(String name); void delete(String name); void update(String name); void select(String name);}
//實現接口需要重寫裏面的方法,類可以實現接口//public class 類名(一般以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 select(String name) { } @Override public void timer() { }}
public interface TimeService { void timer();}
接口小結
- 接口是一種規範、約束
- 只有方法的定義,沒有方法的實現
- 方法默認修飾符爲
public abstract
- 常量默認修飾符爲
public static final
- 接口不能被實例話,接口沒有構造方法
implements
可以實現多個接口- 繼承接口的類必須重寫實現接口的方法
內部類
內部類主要分爲四種:成員內部類、局部內部類、靜態內部類、匿名內部類
內部類就是在一個類的內部定義一個類,比如,A類中定義一個B類,那麼B類相對A類來說就稱爲內部類,而A類相對B類來說就是外部類了。
成員內部類
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); } }}
public static void main(String[] args) { Outer outer = new Outer(); outer.out(); //通過外部類實例話內部類:成員內部類 Outer.Inner inner = outer.new Inner(); inner.in(); inner.getID(); }
靜態內部類
靜態內部類無法直接訪問外部類的非靜態屬性
public class Outer { private int id = 10; public void out(){ System.out.println("這是外部類的方法"); } public static class Inner{ public void in(){ System.out.println("這是內部類的方法"); } //內部類訪問外部私有變量 public void getID(){ System.out.println(id); } }}
還可以在java文件中聲明另一個類
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); } }}//一個java文件裏面只能有一個public class,但是可以有多個classclass A{ }
局部內部類
在類的方法中創建一個類
public class Outer { public void method(){ //局部內部類 class Inner{ } }}
匿名內部類
沒有類名的內部類
public class Test { public static void main(String[] args) { //沒有名字初始化類 //匿名對象的使用,不用把實例保存在變量中 new Apple().eat(); UserService userService = new UserService(){ @Override public void hello() { } }; } }class Apple{ public void eat(){ System.out.println("1"); }}interface UserService{ void hello();}