4.面向?qū)ο?/h3>
- 屬性+方法變成一個類
Java面向?qū)ο髮W習三條主線:
1.Java類及類的成員:屬性,方法,構造器秉扑;代碼塊,內(nèi)部類
2.面向?qū)ο蟮娜筇卣鳎悍庋b性晴叨,繼承性,多態(tài)性矾屯,(抽象性)
3.其他關鍵字:this兼蕊,super,static件蚕,final孙技,abstract产禾,interface,package牵啦,import
- 面向?qū)ο缶幊趟枷?
(類,對象; 面向?qū)ο蟮娜筇卣?...)
- 談談你對面向?qū)ο笾蓄惡蛯ο蟮睦斫?并指出二者關系?
類: 是對一類事物的描述, 抽象的, 概念上的內(nèi)容
對象: 實實在在存在的一個個體. new出來的東西,在內(nèi)存當中存在的, 在內(nèi)存中真正的創(chuàng)建了一個對象,占據(jù)了內(nèi)存一定空間
對象: 是由類派生(new)出來的
4.1.什么是面向?qū)ο?/h4>
面向?qū)ο缶幊?Objected-Oriented-Programming,OOP),Java的核心思想
面向?qū)ο缶幊痰谋举|(zhì)是: 以類的方式組織代碼,以對象的形式組織(封裝)數(shù)據(jù).
抽象 把相似的抽取出來
-
三大特性:
- 封裝 把一個東西打包封裝,留一個口方便把東西拿出
- 繼承 子類可以繼承父類的所有東西,多個子類繼承的東西是一樣的
- 多態(tài) 同一個事物會有多種形態(tài)
從認識論角度考慮是先有對象后有類. 對象,是具體的事物. 類,是抽象的, 是對對象的抽象
從代碼運行角度考慮是先有類后有對象. 類是對象的模板.
值傳遞
// 值傳遞
public static void main(String[] args){
int i = 1;
System.out.println(i);
Demo04.change(i);
System.out.println(i);
i = Demo04.change1(i);
System.out.println(i);
}
// 返回值為空
public static void change(int a){
a = 10;
}
// 返回值不為空
public static int change1(int a){
a = 10;
return a;
}
- 引用傳遞
public class Demo05 {
// 一個類可以有多個public class 但是可以有多個class
// 引用傳遞: 對象,本質(zhì)還是值傳遞
public static void main(String[] args) {
// Person是一個引用亚情,指向的是堆里面的對象
Person person = new Person(); // 實例化Person對象
System.out.println(person.name); // null
// Demo05.change(person);
change(person); // 引用傳遞傳遞的是對象的地址
System.out.println(person.name);// dijia
// 變量名對應的內(nèi)存地址不一樣
// 值傳遞,傳遞后的值被改了不影響原變量
// 因此只是將方法中的變量指向了字符串,并未改變main方法原變量的引用
}
public static void change(Person person) {
// person是一個對象:指向Person這個類
// 或是 Person person = new Person();這是個具體的人,可以改變屬性!
person.name = "dijia";
}
}
// 定義一個Person類,有一個屬性: name
class Person {
String name;
}
4.2.創(chuàng)建與初始化對象
- 面向?qū)ο蟮膬蓚€要素:
- 類:對一類事物的描述,是抽象的哈雏,概念上的定義
- 對象:是實際存在的該類事物的每個個體楞件,因而也稱為實例(instance)
- 類和對象的創(chuàng)建
面向?qū)ο笏枷氲捏w現(xiàn)一 ,類和對象的創(chuàng)建和執(zhí)行操作(面向?qū)ο笏枷肼涞氐貙崿F(xiàn)):
1.創(chuàng)建類,設計類的成員
2.創(chuàng)建類的對象
3.通過"對象.屬性"或"對象.方法()"調(diào)用對象的結構
二,如果創(chuàng)建了一個類的多個對象,每個對象都獨立的擁有一套類的屬性(無static關鍵字的)
意味著,如果我們修改一個對象的屬性a,不影響另一個屬性a的值.(static為可共享屬性)
// 測試類
public class PersonTest {
// main方法作為程序入口
public static void main(String[] args) {
// 創(chuàng)建Person類的對象=類的實例化=實例化類
Person p1 = new Person(); // 類是對象的類型,對象是類的實例,Person是引用的變量類型
// 調(diào)用對象的(功能和行為)結構: 屬性和方法
// 調(diào)用屬性: "對象.屬性;"
p1.name = "tom";
p1.isMale = true;
System.out.println(p1.name);
// 調(diào)用方法: "對象.方法();"
p1.eat();
p1.sleep();
p1.talk("Chinese");
System.out.println("================");
Person p2 = new Person();
System.out.println(p2.name); // null
System.out.println(p2.isMale); // false
System.out.println("===================");
//
Person p3 = p1; // p1對象的地址值賦給了p3,導致p1和p3指向了堆空間中的同一個對象實體
System.out.println(p3.name); // tom
p3.age = 10;
System.out.println(p1);
System.out.println(p3);
System.out.println(p1.age); // 10
}
}
// 創(chuàng)建類,設計類的成員:包括成員方法,成員變量
class Person{
// 屬性= 成員變量= 域,字段
String name;
int age = 1;
boolean isMale;
// 設計方法:行為
public void eat(){
System.out.println("人可以吃飯");
}
public void sleep(){
System.out.println("人可以睡覺");
}
public void talk(String language){
System.out.println("人可以說話使用:" + language);
}
- 對象的內(nèi)存解析:
- 棧的特點:先進后出
1.new Person(); 先造了一個對象,new的結構都在堆里,意味著在堆空間中要造一個對象的實體,該對象會有個地址值稱作首地址值; 在棧空間中聲明了一個變量p1,此時的p1實際是定義在main方法中的,方法中定義的變量都是局部變量,此時,new結構的數(shù)據(jù)賦值給p1,其實賦過來的是個地址,通過賦值地址值后,椛驯瘢空間中的p1就指向了堆空間中造的對象實體,類在設計的時候聲明過三個屬性,這些屬性不同于局部變量,屬性存在于堆空間中,在造好的對象里面,在賦值過程中會給定默認的初始化值和先前賦好的值
2.通過p1找到new的結構調(diào)用name把null改成tom,tom等賦值后的數(shù)據(jù)實際在方法區(qū)中的字符串常量池里
3.重新在堆空間中造一個對象實體, 該實體有一個首地址值,將這個地址值賦值給椡两空間中現(xiàn)在加載的變量p2,棧空間中的p2對象就可以指向堆空間的這個對象實體,對象實體也獨立生成一份默認初始化值和先有的值
4.又在main方法(楉镉蓿空間)中聲明了一個p3局部變量,p3拿p1賦的值,p1存的是地址值,所以傳給p3也是相同地址值,通過p1傳過來的地址值,p3也順著地址指向p1的在堆空間中的結構,所以p3不能叫新創(chuàng)建的一個對象,只能算新聲明的一個變量,此時p1和p3指向了堆空間中的同一個對象實體
4.3.屬性與局部變量的對比
類中屬性的使用
屬性(成員變量) vs 局部變量
- 1.相同點:
- 1.1.定義變量的格式: 數(shù)據(jù)類型 變量名 = 變量值
- 1.2.先聲明,后使用
- 1.3.變量都有其對應的作用域
- 2.不同點:
- 2.1.在類中聲明的位置不同
屬性:直接定義在類的一對{}里
局部變量:聲明在方法內(nèi),方法形參,代碼塊內(nèi),構造器形參,構造器內(nèi)部的變量
- 2.2.關于權限修飾符的不同
屬性:可以在聲明屬性時,指明其權限,使用權限修飾符.
常用的權限修飾符: private,public,缺省(相當于默認,default),protected --->類的封裝性
目前,聲明屬性時,使用缺省就行了
局部變量:不可以使用權限修飾符,可理解為其權限被方法的權限代替了
- 2.3.默認初始化值的情況:
屬性: 類的屬性,根據(jù)其類型,都有默認初始化值.
整型(byte,short,int,long): 0
浮點型(float,double): 0.0
字符型(char), 0 (或'\u0000')
布爾型(boolean): false
引用數(shù)據(jù)類型:(類(特殊的String),數(shù)組,接口): null
局部變量: 沒有初始化值,意味著調(diào)用局部變量之前,一定要顯示賦值
特別地: 形參在調(diào)用時,賦值即可.
- 2.4.二者在內(nèi)存中加載的位置:
(非static)屬性:加載到堆空間中(static屬性都放在方法區(qū)中)
局部變量:加載到椪て空間中
public static void main(String[] args) {
User u1 = new User();
System.out.println(u1.name);
System.out.println(u1.age);
System.out.println(u1.isMale);
u1.talk("中文");
u1.eat();
}
}
class User{
// 屬性(成員變量)
String name; // private權限小,出了定義的這個類就不能調(diào)了
public int age; // public權限大,在外的就能調(diào)
boolean isMale;
public void talk(String language){ // language:形參(局部變量)
System.out.println("我們使用" + language + "交流");
}
public void eat(){
String food = "烙餅"; // (聲明)定義在方法內(nèi)的變量叫局部變量
System.out.println("北方人吃" + food);
}
4.4.空指針異常(NullPointerException)
關于垃圾回收器: GC
在Java語言中,垃圾回收器主要針對的是堆內(nèi)存.
當一個Java對象沒有任何引用指向該對象的時候,
GC會考慮將該垃圾數(shù)據(jù)釋放回收掉.
- 出現(xiàn)空指針異常的前提條件是?
- "空引用"訪問實例(對象相關,例如id)相關的數(shù)據(jù)時,都會出現(xiàn)呢空指針異常.
/*
空指針異常
*/
public class NullPointerTest {
public static void main(String[] args) {
// 創(chuàng)建客戶對象
Customer1 c1 = new Customer1();
// 訪問這個客戶對象
System.out.println(c1.id);
// 重新給id賦值
c1.id = 242;
System.out.println(c1.id);
c1 = null;
// NullPointerException 空指針異常
// 編譯器沒問題,因為編譯器只檢查語法,編譯器發(fā)現(xiàn)c是Customer類型,
// Customer類型中由id屬性,所以可以: 調(diào)用c.id,語法過了,
// 但是運行的時候需要對象的存在,但是對象丟失,就只能出現(xiàn)異常
System.out.println(c1.id);
}
}
// 客戶類
class Customer1{
// 客戶id屬性
int id; // 成員變量中的實例變量,應該先創(chuàng)建對象,再通過"引用."的方式訪問
}
4.5.對象數(shù)組
public class StudentTest1 {
public static void main(String[] args) {
// 聲明自定義類Student類型的對象數(shù)組
Student1[] stus = new Student1[20]; // 類似于String[] arr = new String[];
for (int i = 0;i < stus.length;i++){
// 給數(shù)組元素賦值
stus[i] = new Student1();
// 給Student對象的屬性賦值
stus[i].number = (i + 1);
// 年級: [1,6]
// 隨機數(shù)a~b公式: int value = (int)(Math.random() * (b - a + 1) - a)
stus[i].state = (int)(Math.random() * (6 - 1 + 1) + 1);
// 分數(shù): [0~100]
stus[i].score = (int)(Math.random() * (100 - 0 + 1));
}
// 想在main方法里調(diào)用其他方法,要在main方法公共類里造一個當前類的對象,
StudentTest1 test = new StudentTest1();
// 遍歷學生數(shù)組
test.print(stus);
/*for (int i = 0;i < stus.length;i++){
// stus[i].info();
System.out.println(stus[i].info2());
}*/
System.out.println("======================");
// 打印三年級(state值為3)的學生信息
test.searchState(stus,3);
/*for (int i = 0;i < stus.length;i++){
if (stus[i].state == 3){
stus[i].info();
}
}*/
System.out.println("========================");
// 使用冒泡排序按學生成績排序,并遍歷所有學生信息
/* for (int i = 0;i < stus.length - 1;i++){
for (int j = 0;j < stus.length - 1 - i;j++){
if (stus[j].score > stus[j+1].score){
// 這里要交換數(shù)組元素的對象!意思是把整個人交換
// 如果只交換成績,學號和年級都沒換,相當于"作弊"
Student1 temp = stus[j];
stus[j] = stus[j+1];
stus[j+1] = temp;
}
}
}*/
/*for (int i = 0;i < stus.length;i++){
System.out.println("學號:" + stus[i].number + "年級:" + stus[i].state + "成績:"+ stus[i].score);
}*/
}
// 遍歷Student1[]類型數(shù)組的操作
/**
* @Description 遍歷Student1[]類型數(shù)組的操作
* @author tiga
* @date time
* @param stus
*/
public void print(Student1[] stus){ // 傳入Student1[]類型數(shù)組的形參
for (int i = 0;i < stus.length;i++){
stus[i].info();
}
}
// 查找某年級的學生信息
/**
*
* @Description 查找Student類型數(shù)組中指定年級的學生信息
* @author tiga
* @date time
* @param stus 要查找的數(shù)組
* @param state 要查找的年級
*/
public void searchState(Student1[] stus,int state){ // 傳入要找的那個數(shù)組和要找的年級為形參
for (int i = 0;i < stus.length;i++){
if (stus[i].state == state){
System.out.println(stus[i].info2());
}
}
}
// 冒泡排序?qū)W生成績,遍歷學生信息
public void sort(Student[] stus){
for (int i = 0;i < stus.length - 1;i++){
for (int j = 0;j < stus.length - 1 - i;j++){
if (stus[j].score > stus[j+1].score){
Student temp = stus[j];
stus[j] = stus[j+1];
stus[j+1] = temp;
}
}
}
}
}
class Student1{
int number; // 學號
int state; // 年級
int score; // 成績
public void info(){
System.out.println("學號:" + number + "年級:" + state + "成績:" + score);
}
public String info2(){
return "學號:" + number + "年級:" + state + "成績:" + score;
}
}
- 對象數(shù)組的內(nèi)存解析
引用類型變量只能存儲null或?qū)ο蟮牡刂分?含變量的類型).
4.6.匿名對象
一.理解"萬事萬物皆對象"
1.在Java語言范疇中,都將功能,結構等封裝到類中,通過類的實例化,來調(diào)用具體的功能結構
Scanner,String
文件:File
網(wǎng)絡資源: URL
2.涉及到Java語言與前端HTML,后端的數(shù)據(jù)庫交互時,前后端的結構在Java層面交互時,都體現(xiàn)為類,對象
二.內(nèi)存解析的說明:
1.引用類型的變量,只可能存儲兩類值:null 或 地址值(含變量的類型)
三.匿名對象的使用
1.理解:創(chuàng)建的對象,沒有顯式的賦給一個變量名.即為匿名對象
2.特征: 匿名對象只能調(diào)用一次
3.使用: 可以將匿名對象放入方法里多次調(diào)用
- 內(nèi)存簡單分析
在調(diào)用方法里new完Phone這個類的對象,在堆里產(chǎn)生的空間也會有一個地址值,調(diào)用時new的對象其實是賦值給了定義類里的方法里作為形參,形參又是個局部變量,且該變量會存入棧中,相當于把new Phone這個類出來的對象的地址值賦給了形參,只要是賦給了變量,對象就可以多次使用,從而形參就指向了堆中new的新對象, 就能通過新對象的地址調(diào)用其他方法, 這時調(diào)用的是同一個匿名對象,這相當于把匿名的對象賦給了有名的對象(形參).
public class InstanceTest {
public static void main(String[] args) {
Phone p = new Phone(); // p就是Phone對象的變量名
// p = null;
System.out.println(p);
p.sentEmail();
p.playGame();
// 匿名對象
// 兩者調(diào)用的不是同一對象
new Phone().sentEmail();
new Phone().playGame();
new Phone().price = 2000;
new Phone().showPrice(); // 0.0
System.out.println("======================");
// 匿名對象的使用
PhoneMall mall = new PhoneMall();
mall.show(new Phone()); // 理論上是匿名
ShowAddress add = new ShowAddress();
add.printAdd(new PhoneMall());
}
}
class ShowAddress{
public void printAdd(PhoneMall phone){
System.out.println(phone);
}
}
class PhoneMall{
public void show(Phone phone){ // 實際上形參就是匿名函數(shù)的名字
phone.sentEmail();
phone.playGame();
}
}
class Phone{
double price; // 價格
public void showPrice(){
System.out.println("手機價格是:" + price);
}
public void sentEmail(){
System.out.println("發(fā)送郵件");
}
public void playGame(){
System.out.println("玩游戲");
}
}
- 理解變量的賦值
- 當在方法中定義了基本數(shù)據(jù)類型的局部變量,其變量存放在棧中,給變量賦值的數(shù)字就是變量在棧內(nèi)存中存放的東西, 變量之間的賦值傳遞, 也是傳遞數(shù)字.
int m = 10;
int n = m; // n == 10
- 當在方法中定義了引用類型的局部變量,其變量也是存放在棧中,但是變量賦給的值的地址值, 所以當同類型變量之間賦值傳遞, 是傳遞地址,而不是數(shù)字.
- 值傳遞機制: 針對基本數(shù)據(jù)類型
一.方法的形參的傳遞機制: 值傳遞
1.形參: 方法定義時,聲明的小括號內(nèi)的參數(shù)
2.實參,方法調(diào)用時,實際傳遞給形參的數(shù)據(jù),可以是具體數(shù)也可以是變量
二.值傳遞機制: 如果參數(shù)是基本數(shù)據(jù)類型,此時實參賦給形參的是實參真實存儲的數(shù)據(jù)值.
- 內(nèi)存分析.
1.m和n沒有換成的原因: 在棧中,先從main方法進來,main方法中定義的m, n分別賦值10, 20;
2.通過對象v調(diào)swap方法,v就不往里面寫了,當調(diào)用swap方法時候,將上面實參m, n表示數(shù)據(jù)的值,賦給了下面swap方法的形參m, n(新定義的),這兩個形參還要在棧中提供,形參m, n分別是實參賦值過來的,也是10和20,此時的m, n 是swap方法中的;
3.接著v調(diào)用swap方法就進入方法執(zhí)行,一進去又聲明新變量temp,temp拿了形參m賦的值,接著形參n的值給了m,接著又把temp給了n,最swap方法實現(xiàn)效果是把swap方法里的m和n交換,如果在方法里面輸出,m和n就會交換,但是如果方法內(nèi)不輸出,swap方法執(zhí)行完以后就銷毀了(出棧), 銷毀之后接著在main方法下面再輸出,輸出的m和n的值是main方法賦的值,因為swap方法出棧以后,輸出的m和n保存的值是main里面定義的m和n兩變量的值, main里面定義的變量在main的整個大括號區(qū)間里都是有效的,所以main里面輸出的還是main里定義的值,所以兩數(shù)沒換成. 數(shù)組元素的值交換也是同理
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println(m + "," + n);
ValueTransferTest1 test = new ValueTransferTest1();
test.swap(m,n); // 實參
/*int temp = m;
m = n;
n = temp;*/
System.out.println(m + "," + n); // 10, 20
}
public void swap(int m,int n){ // 新變量實參傳入的形參
int temp = m;
m = n;
n = temp;
// System.out.println(m + "," + n);
}
值傳遞機制: 針對引用數(shù)據(jù)類型
靜態(tài)方法調(diào)用非靜態(tài)方法要先new一個對象
靜態(tài)方法調(diào)用靜態(tài)方法直接調(diào)用
非靜態(tài)方法調(diào)用非靜態(tài)方法直接調(diào)用
內(nèi)存分析
1.從main方法進來,在棧中new了一個data變量,也在堆空間中new了一個對象,該對象會有個地址值,并把地址值賦值給椪竟剩空間的data變量,通過地址值,椊耘拢空間中的data變量指向堆空間中的對象實體
2.接著下一步,data(對象)變量調(diào)用Data類中定義的變量m, n,一開始m, n的初始值都為0,通過對象調(diào)屬性改了m, n的值,此時輸出就是10, 20.
3.接著新建的對象去調(diào)swap方法,順便把引用類型data傳入作為形參,傳入的引用類型data是上面的實參,所以形參是引用類型就存的是地址值,形參在棧中加載,相當于把main方法中data的地址值復制了一份,形參有了地址值后,他就指向堆空間中同一對象實體
4.進入swap方法體,通過data(形參)變量調(diào)用m的值,賦給一個swap方法內(nèi)部聲明的局部變量temp,temp也加載在棧中,data的n的值賦給data的m,temp的值又賦給了data的n,至此方法執(zhí)行結束,結束后,swap方法定義的temp和data變量就出棧了,出棧后形參data的指針就沒了,但是堆中對象還在,還有實參data的指向,這樣判斷堆中對象就不是垃圾,不能回收了.
5.回到main方法接著執(zhí)行輸出 data.m和data.n, 這兩data變量是實參data, m和n的值就交換了
- 練習: 方法的參數(shù)傳遞
4.7.面向?qū)ο蟮奶卣饕? 封裝和隱藏
面向?qū)ο蟮奶卣饕? 封裝和隱藏
一.問題的引入:
當我們創(chuàng)建一個類的對象以后,我們可以通過"對象.屬性"的方式,對對象的屬性進行賦值,這里,賦值操作要瘦到屬性的數(shù)據(jù)類型和存儲范圍的制約
除此之外,沒有其他制約條件,但是在實際問題中,我們往往需要給屬性賦值加入額外的限制調(diào)價.這個特條件就不能在屬性聲明時體現(xiàn),我們只能通過方法進行限制條件的添加.(比如: setLegs())
同時,我們需要避免用戶再使用"對象.屬性"的方式對屬性進行賦值.則需要將屬性聲明為私有的(private)
-->此時,針對于屬性就體現(xiàn)了封裝性
二.封裝性的體現(xiàn)之一(不等同于封裝性):
將類的屬性xxx私有化(private),同時,提供公共的(public)方法來獲取(getXxx)和設置(setXxx)此屬性的值
拓展: 封裝性的體現(xiàn): 1.如上 2.不對外暴露的私有的方法 3.單例模式: 把構造器私有化
三.封裝性的體現(xiàn),需要權限修飾符來配合
1.Java規(guī)定的4種權限(從小到大排列): private,缺省,protected,public
2.4種權限可以用來修飾類及類的內(nèi)部結構: 屬性,方法,構造器,內(nèi)部類,(代碼塊不行)
3.具體的,4中權限都可以用來修飾類的內(nèi)部結構:屬性,方法,構造器,內(nèi)部類
修飾類的話,只能用: 缺省,public,不能用private
總結封裝性: Java提供了4中權限修飾符來修飾類及類的內(nèi)部結構,體現(xiàn)類及類的內(nèi)部結構在被調(diào)用時的可見性的大小
體現(xiàn)一: 將類的屬性xxx私有化(private),同時,提供公共的(public)方法來獲取(getXxx)和設置(setXxx)此屬性的值
體現(xiàn)二: 不對外暴露的私有的方法
體現(xiàn)三:單例模式(將構造器私有化,外邊不能隨便調(diào)構造器, 里面自己只造一個對象,大家都只拿一個對象用)
體現(xiàn)四: 如果不希望類在包外被調(diào)用,可以將類設置為缺省的.
public class AnimalTest {
public static void main(String[] args) {
Animal a = new Animal();
a.name = "大黃";
// a.age = 1; // 將屬性私有化后不能直接調(diào)用,要通過public方法調(diào)用
// a.legs = 4; // 作用域不可見 The field Animal.legs is not visible
a.show(); // 誰調(diào)的show方法,方法里顯示的屬性值就是誰的
a.setLegs(-6);
// a.legs = -4; // The field Animal.legs is not visible
a.show();
}
}
class Animal{
String name;
private int age;
// private好處:setLegs提供方法讓legs賦值并加入判斷條件,還能把直接調(diào)屬性給禁掉
// 此時就能達到對 legs屬性的封裝,相當于這個屬性沒有對外暴露
private int legs; // 腿的數(shù)量 限制為私有權先,可見但類外面不能調(diào)用
public void setLegs (int l){
if (l >= 0 && l % 2 == 0){
legs = l;
}else{
legs = 0;
// 另一種寫法,拋出一個異常(暫時沒講)
}
}
public int getLges(){
return legs;
}
public void eat(){
System.out.println("動物進食");
}
public void show (){
System.out.println("name = " + name + ",age = " + age + ",legs = " + legs);
}
// 提供關于屬性age的get和set方法
public int getAge(){
return age;
}
public void setAge(int a){
age = a;
}
}
- 練習: 封裝性的基本使用
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
// p1.age = 1; // age私有化后不能直接調(diào)用,編譯不通過
p1.setAge(12);
System.out.println("年齡為:" + p1.getAge());
}
}
public class Person {
private int age;
public void setAge(int a){
if (a < 0 || a > 130){
// throw new RuntimeException("傳入數(shù)據(jù)非法"); // [[拋異常
System.out.println("傳入數(shù)據(jù)非法");
return; // 結束方法
}
age = a;
}
public int getAge(){
return age;
}
// 絕對不要把設置和獲取的方法合起來寫! 這些寫方法沒意義
/*public int doAge(int a){
age = a;
return a;
}*/
}
4.8.類的成員之三: 構造器(或構造方法)
- 類的結構之三,構造器(或構造函數(shù)(c++)或構造方法但又不同于方法,constructor)的使用
construct: 建設,建造. construction constructor: 建設者
- 一.構造器的作用:
1.創(chuàng)建對象
2.初始化對象的屬性
- 二.說明:
1.如果沒有顯式的定義類的構造器的話,則系統(tǒng)默認提供默認無參構造器
2.定義構造器的格式(不同于方法): 權限修飾符 類名(形參列表){}
3.一個類中定義多個構造器,彼此構成重載
4.一旦我們顯式的定義了類的構造器之后,系統(tǒng)就不在提供默認的無(空)參構造器(可用國家貧困補貼舉例)
5.一個類中,至少會有一個構造器.(相當于給我們造對象的能力,有構造器才能造對象)(可能是默認的也可是自己寫的)
public class PersonTest {
public static void main(String[] args) {
// 創(chuàng)建類的對象(構造器的使用): new + 構造器 (構造器和類同名)
Person p = new Person(); // 自己定義了顯式的構造器
p.eat();
Person p1 = new Person("tom");
System.out.println(p1.name);
}
}
class Person{
int age;
String name;
// 構造器
public Person(){
// 可以用于初始化 比如實例化這個類時就需要傳值,而不是在調(diào)用方法才傳值
// 構造器里的代碼在new對象的時候就執(zhí)行了
System.out.println("Person().....");
}
// 構造器的重載
public Person(String n){ // 形參的作用 初始化造的對象當前的屬性
name = n; // name: 當前正在創(chuàng)建的對象的屬性
}
public Person(String n, int a){
name = n;
age = a;
}
public void eat(){
System.out.println("人吃飯");
}
public void study(){
System.out.println("人睡覺");
}
}
- 練習: 構造器的基本使用
1.創(chuàng)建程序,在其中定義兩個類:Person和PersonTest類.定義如下:
用setAge()設置熱的合法年齡(0~130),用getAge()返回人的年齡
2.1.在前面定義的Person類中添加構造器,利用構造器設置所有人的age屬性初始值都為18
2.2.修改上題中類和構造器,增加name屬性,使得每次創(chuàng)建Person對象的同時初始化對象的age屬性值和name屬性值
3.如果類中提供了至少一個構造器,但是沒有提供空參構造器,那么構造對象時如果不提供參數(shù)就是不合法的.也就是創(chuàng)建沒有形參的對象會報錯
/*
* 在PersonTest類中實例化Person類的對象b
* 調(diào)用setAge()和getAge()方法,體會Java的封裝性
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
// p1.age = 1; // age私有化后不能直接調(diào)用,編譯不通過
p1.setAge(12);
// p1.getAge(); // age == 18
System.out.println("年齡為:" + p1.getAge());
Person p2 = new Person("tom", 21);
// 屬性私有化后只能通過方法來調(diào)用
System.out.println("name: " + p2.getName() + ", age: " + p2.getAge());
}
}
public class Person {
// 代碼順序: 屬性-->構造器-->方法
private int age;
private String name;
public Person(){
age = 18;
}
// 體現(xiàn)了有參構造器
public Person(String n, int a){
name = n;
age = a;
}
public void setAge(int a){
if (a < 0 || a > 130){
// throw new RuntimeException("傳入數(shù)據(jù)非法"); // 拋異常
System.out.println("傳入數(shù)據(jù)非法");
return; // 結束方法
}
age = a;
}
public int getAge(){
return age;
}
// 體現(xiàn)了封裝性
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
// (錯誤寫法)絕對不要把設置和獲取的方法合起來寫! 這些寫方法沒意義
/*public int doAge(int a){
age = a;
return a;
}*/
}
- 構造器練習: 三角形
/*
* 編寫兩個類,TriAngle和TriAngleTest,其中TriAngle類中聲明私有的底邊長base和高height,同時聲明公共方法訪問私有變量.
* 此處,提供類必要的構造器.另一個類中使用這些公共方法,計算三角形的面積
*/
public class TriAngleTest {
public static void main(String[] args) {
TriAngle t1 = new TriAngle();
t1.setBase(3.2); // 相當于傳入實參
t1.setHeight(4.66);
System.out.println("base: " + t1.getBase() + ", height: " + t1.getHeight());
// 構造器一方面可創(chuàng)建對象,同時可給相應對象的屬性賦值
TriAngle t2 = new TriAngle(5.1, 4.6);
System.out.println("base: " + t2.getBase() + ", height: " + t2.getHeight());
}
}
public class TriAngle {
private double base;
private double height;
public TriAngle(){
}
public TriAngle(double b,double h){
base = b;
height = h;
}
public void setBase(double b){
base = b;
}
public double getBase(){
return base;
}
public void setHeight(double h){
height = h;
}
public double getHeight(){
return height;
}
}
- 總結: 屬性賦值的先后順序
1.默認初始化(類屬性默認值)
2.顯式初始化(給類中屬性賦值)
3.構造器中初始化(賦值)
=======上面三個操作只能執(zhí)行一次,下面的可以反復執(zhí)行=========
4.未封裝通過"對象.屬性"的方式,賦值或封裝后通過"對象.方法名()",賦值
以上操作的先后順序: 1 - 2 - 3 - 4,從后往前覆蓋,前面的都作為過程出現(xiàn)
public class UserTest {
public static void main(String[] args) {
User u = new User();
System.out.println(u.age);
User u1 = new User(2);
u1.setAge(3);
System.out.println(u1.age);
}
}
class User{
String name;
int age = 1;
public User(){
}
public User(int a){
age = a;
}
public void setAge(int a){
age = a;
}
}
4.9.關鍵字: this的使用---this調(diào)用屬性和方法
this關鍵字的使用:
- 1.this可用來修飾: 屬性,方法,構造器
- 2.this修飾屬性和方法:
this理解為: 當前對象或當前正在創(chuàng)建的對象
- 2.1.在類的方法中,我們課使用"this.屬性"或"this.方法"的方式,調(diào)用當前對象屬性或方法.
但是,通常情況下,我們都選擇省略"this. ".特殊情況下,如果方法的形參和類的屬性同名時,必須顯式的使用"this.變量"的方式,表明此變量是屬性,而非形參.
- 2.2.在類的構造器中,我們可使用"this.屬性"或"this.方法"的方式,調(diào)用當前正在創(chuàng)建的對象屬性或方法,但是,通常情況下,都選擇省略"this.",特殊情況下,如果構造器的形參和類的屬性同名時,必須顯式的 使用"this.變量"的方式,表明此變量是屬性,而非形參.
public class PersonTest {
public static void main(String[] args){
Person p1 = new Person(); // 使用默認的空參構造器
p1.setAge(1);
System.out.println(p1.getAge());
p1.eat();
}
}
class Person{
private String name; // 類的成員變量(屬性)
private int age;
public Person(){
this.eat(); // 調(diào)當前正在創(chuàng)建的對象的eat方法
}
// 構造器中的this: 當前正在創(chuàng)建的對象
public Person(String name){
this.name = name; // 當前正在創(chuàng)建的對象的屬性
}
public Person(int age){
this.age = age;
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name){ // 屬性和形參(局部變量)同名不報錯因為地位不同,一個在類中一個在方法中
this.name = name; // 就近原則,優(yōu)先考慮近的變量名
}
public String getName(){
return this.name;
}
public void setAge(int age){ // age = 1
// age = age; // 1 = 1,跟屬性age無關了
// this: 可理解為當前對象,相當于p1
this.age = age; // 當前對象的屬性/方法 通過對象調(diào)方法或?qū)傩远加命c
}
public int getAge(){
return this.age;
}
public void eat(){
System.out.println("人吃飯");
this.study();
}
public void study(){
System.out.println("人學習");
}
}
-
this調(diào)用構造器
this關鍵字的使用:
1.this可用來修飾: 屬性,方法,構造器
2.this修飾屬性和方法:
this理解為: 當前對象或當前正在創(chuàng)建的對象
2.1.在類的方法中,我們課使用"this.屬性"或"this.方法"的方式,調(diào)用當前對象屬性或方法.
但是,通常情況下,我們都選擇省略"this. ".特殊情況下,如果方法的形參和類的屬性同名時,
必須顯式的使用"this.變量"的方式,表明此變量是屬性,而非形參.
2.2.在類的構造器中,我們可 使用"this.屬性"或"this.方法"的方式,調(diào)用當前正在創(chuàng)建的對象屬性或方法,
但是,通常情況下,都選擇省略"this.",特殊情況下,如果構造器的形參和類的屬性同名時,必須顯式的
使用"this.變量"的方式,表明此變量是屬性,而非形參.
3.this調(diào)用構造器
(1)在類的構造器中,可顯式的使用"this(形參列表)"方式,調(diào)用本類中指定的其他構造器
(2)構造器中不能通過"this(形參列表)"方式調(diào)用自己
(3)如果一個類中由n個構造器,則最多有n-1個構造器中使用了"this(形參列表)"
(4)規(guī)定: "this(形參列表)"必須聲明當前構造器的首行
(5)構造器內(nèi)部,最多只能聲明一個"this(形參列表)",用來調(diào)用其他的構造器
public class PersonTest {
public static void main(String[] args){
Person p1 = new Person(); // 使用默認的空參構造器
p1.setAge(1);
System.out.println(p1.getAge());
p1.eat();
System.out.println();
// 構造器多重調(diào)用也只是創(chuàng)建了一個對象,只是調(diào)用其他構造器時邏輯借用了其他構造器的邏輯
// 不管通過什么方式調(diào)用構造器,最終也只是造了一個對象
Person p2 = new Person("Harry", 20);
System.out.println(p2.getAge());
System.out.println(p2.getName());
}
}
class Person{
private String name; // 類的成員變量(屬性)
private int age;
public Person(){
// this.eat(); // 調(diào)當前正在創(chuàng)建的對象的eat方法
// this(); // 空參構造器不能調(diào)自己,會死循環(huán),兩個構造器也不能來回調(diào)
String info = "Person初始化時,需要考慮如下的1,2,3,4...(共40行代碼)";
System.out.println(info);
}
// 構造器中的this: 當前正在創(chuàng)建的對象
public Person(String name){
this(); // 調(diào)用構造器的方法,參數(shù)是什么,就是調(diào)哪個構造器
this.name = name; // 當前正在創(chuàng)建的對象的屬性
}
public Person(int age){
this();
this.age = age;
}
public Person(String name, int age){
this(age);
this.name = name; // 再初始化獨立的name
// this.age = age; 調(diào)用上面的構造器后可省略
}
public void setName(String name){ // 屬性和形參(局部變量)同名不報錯因為地位不同,一個在類中一個在方法中
this.name = name; // 就近原則,優(yōu)先考慮近的變量名
}
public String getName(){
return this.name;
}
public void setAge(int age){ // age = 1
// age = age; // 1 = 1,跟屬性age無關了
// this: 可理解為當前對象,相當于p1
this.age = age; // 當前對象的屬性/方法 通過對象調(diào)方法或?qū)傩远加命c
}
public int getAge(){
return this.age;
}
public void eat(){
System.out.println("人吃飯");
this.study();
}
public void study(){
System.out.println("人學習");
}
}
- 綜合練習
public class CustomerTest {
public static void main(String[] args) {
Customer cust = new Customer("Jane", "Smith");
Account acct = new Account(1000, 2000, 0.0123); // 聲明構造器
cust.setAccount(acct); // 銀行辦卡后得到了卡號
cust.getAccount().deposit(100); // 類變量的運用
cust.getAccount().withdraw(960);
cust.getAccount().withdraw(2000);
System.out.println("Customer [" + cust.getLastName() + ","
+ cust.getFirstName() + "] has a account: id is "
+ cust.getAccount().getId() + ", annualInterrestRate is"
+ cust.getAccount().getAnnualInterestRate() * 100
+ "%, balance is " + cust.getAccount().getBalance());
}
}
public class Account {
private int id; // 賬號
private double balance; // 余額
private double annualInterestRate; // 年利率
// 構造器
public Account(int id,double balance,double annualInterestRate){
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public void setId(int id){
this.id = id;
}
public int getId(){
return this.id;
}
public void setBalance(double balance){
this.balance = balance;
}
public double getBalance(){
return this.balance;
}
public void setAnnualInterestRate(double annualInterestRate){
this.annualInterestRate = annualInterestRate;
}
public double getAnnualInterestRate(){
return this.annualInterestRate;
}
public void withdraw(double amount){ // 取錢
if (balance < amount){
System.out.println("余額不足,取錢失敗");
return; // 和else作用相同
}
balance -= amount;
System.out.println("成功取出" + amount);
}
public void deposit(double amount){ // 存錢
if (amount > 0){
balance += amount;
System.out.println("成功存入" + amount);
}
}
}
public class Customer {
private String firstName;
private String lastName;
private Account account;
// 構造器
public Customer(String f,String l){
this.firstName = f;
this.lastName = l;
}
public String getFirstName(){
return this.firstName;
}
public String getLastName(){
return this.lastName;
}
public void setAccount(Account account){
this.account = account;
}
public Account getAccount(){
return this.account;
}
}
- 綜合練習2: 對象數(shù)組
public class BankTest {
public static void main(String[] args) {
Bank bank = new Bank();
bank.addCustomer("Jane","Smith");
// 連續(xù)操作 可能會空指針,方法要有返回值才能連續(xù)操作
bank.getCustomer(0).setAccount(new Account(2000));
bank.getCustomer(0).getAccount().withdraw(500);
double balance = bank.getCustomer(0).getAccount().getBalance();
System.out.println("客戶:" + bank.getCustomer(0).getLastName() + "的賬戶余額為: " + balance);
bank.addCustomer("萬里","楊");
System.out.println("銀行客戶個數(shù): " + bank.getNumberOfCustomers());
}
}
public class Customer {
private String firstName;
private String lastName;
private Account account;
// 構造器
public Customer(String f,String l){
this.firstName = f;
this.lastName = l;
}
// get,set方法
public String getFirstName(){
return this.firstName;
}
public String getLastName(){
return this.lastName;
}
public void setAccount(Account acct){
this.account = acct;
}
public Account getAccount(){
return this.account;
}
}
public class Account {
private double balance;
public Account(double init_balance){
this.balance = init_balance;
}
// 余額查詢
public double getBalance(){
return this.balance;
}
// 存錢操作
public void deposit(double amt){
if (amt > 0){
balance = balance + amt;
System.out.println("存款成功");
}
}
// 取錢操作
public void withdraw(double amt){
if (balance > amt){
balance = balance - amt;
System.out.println("取錢成功");
}else{
System.out.println("余額不足");
}
}
}
public class Bank {
// 定義了變量,未初始化,但是屬性是已經(jīng)默認初始化了的,此時默認值為null
private Customer[] customers; // 存放多個用戶的數(shù)組
private int numberOfCustomers; // 記錄客戶的個數(shù)
// 構造器
public Bank(){
customers = new Customer[10]; // 先要給數(shù)組初始化空間 因為權限是private所以不能再測試類main里初始化
}
// 添加客戶方法
public void addCustomer(String f,String l){
// cust是customer類型變量,上面定義的數(shù)組也是customer類型的,所以可以賦值
Customer cust = new Customer(f, l);
// customers[numberOfCustomers] = cust; // 可以理解為 customers[numberOfCustomers] = new Customer(f,l); 數(shù)組元素在構造器中靜態(tài)賦值
// numberOfCustomers++;
// customers = new Customer[10]; // 數(shù)組空間初始化不能寫在這,因為,本來是是造構造器中造好一個數(shù)組后掉一個方法存一個人再調(diào)方法再存一個人,寫在方法里變成,先調(diào)方法造好一個數(shù)組后再存一個人,再調(diào)一次方法又重新造一個數(shù)組再重新存一個人
customers[numberOfCustomers++] = cust;
}
// 獲取客戶個數(shù)的方法
public int getNumberOfCustomers(){
return numberOfCustomers;
}
// 獲取指定位置上的客戶的方法
public Customer getCustomer(int index){
// return customers[index]; // 不能這樣寫,可能報異常1.數(shù)組未初始化時可能空指針,2.數(shù)組初始化后可能會數(shù)組下標越界,3.index也許不會超出數(shù)組長度,但是大于實際存入的數(shù)組元素個數(shù)也不行.
if (index >= 0 && index < numberOfCustomers){ // 索引值小于客戶數(shù),因為第一個客戶放在了第0位
return customers[index];
}
return null;
}
}
4.10.1.package關鍵字的使用
- 1.為了更好地實現(xiàn)項目中類的管理,提供包的概念
- 2.使用package聲明類或接口所屬的包,聲明在源文件的首行
- 3.包,屬于標識符,遵循標識符的命名規(guī)則,規(guī)范(xxx.yyy.zzz),"見名知意"
- 4.每"."一次,就代表一層文件目錄.
補充:同一個包下不能命名同名的接口,類
不同的包下,可以命名同名的接口,類
4.10.2.import關鍵字的使用
5.1.面向?qū)ο筇卣髦? 繼承性的理解
1.為什么要有類的繼承性?(繼承性的好處)
1.減少了代碼的冗余,提高了代碼的復用性
2.便于功能的擴展: 把子類都想擴展的功能直接聲明在父類當中,子類又因為繼承了,直接就可以拿到
3.為之后多態(tài)性的使用,提供了前提
2.繼承性的格式:
class A extends B{}
A: 子類,派生類,subclass
B: 父類,超類,基類,superclass
3.子類繼承父類以后有哪些不同?
2.1.體現(xiàn): 一旦子類A繼承父類B以后,子類A中就獲取了父類B中聲明的所有:屬性,方法 (用來調(diào)用)
特別地,父類中聲明為private的屬性或方法,子類繼承父類以后,仍然認為獲取了父類中私有的結構
只有因為封裝性的影響,使得子類不能直接調(diào)用父類的結構而已
2.2.子類繼承父類以后,還可以聲明自己特有的屬性或方法,實現(xiàn)自己特有的功能的拓展
子類和父類的關系,不同于子集和集合的關系(父類有的子類一定有,子類有的父類不一定有)
4.Java中繼承的說明
1.一個父類可以被多個子類繼承.
2.Java中類的單繼承性:一個類只能有一個父類;(接口可以多繼承)
3.子父類是相對的概念
4.子類直接繼承的父類,稱為: 直接父類;簡介繼承的父類稱為:間接父類
5.子類繼承父類后,就獲取了直接父類以及所有間接父類中聲明的屬性和方法
public class ExtendsTest {
public static void main(String[] args) {
Person p1 = new Person();
// p1.age = 1;
p1.eat();
Student s1 = new Student();
// s1.age = 1;
s1.eat(); // 私有化的方法也被繼承,
// s1.sleep();
s1.setAge(10);
System.out.println(s1.getAge());
s1.breath();
Creature c = new Creature();
System.out.println(c.toString());
}
}
public class Creature {
public void breath(){
System.out.println("呼吸");
}
}
public class Person extends Creature {
private int age;
String name;
// 定義構造器
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
// 定義方法
public void eat(){
System.out.println("吃飯");
sleep();
}
private void sleep(){
System.out.println("睡覺");
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return this.age;
}
}
// Student繼承Person類的屬性,功能...
public class Student extends Person{
// Person中定義過的可以省略
/*String name;
int age;*/
String major; // 專業(yè) 對繼承的不滿意可以自己創(chuàng)建新的
// 定義構造器 和類重名不能刪
public Student(){
}
public Student(String name,int age, String major){
this.name = name;
setAge(age);
this.major = major;
}
// 定義方法
/*public void eat(){
System.out.println("吃飯");
}
public void sleep(){
System.out.println("睡覺");
}*/
// 獨有的方法也不能刪
public void study(){
System.out.println("學習");
}
public void show(){
System.out.println("name: " + name + ", age = " + getAge());
}
}
- 繼承性練習:
public class CylinderTest {
public static void main(String[] args) {
Cylinder cy = new Cylinder();
cy.setRadius(1.2);
cy.setLength(3.4);
double volume = cy.findVolume();
System.out.println("圓柱的體積為: " + volume);
// 沒有重寫findArea方法時
/*double area = cy.findArea();
System.out.println("底面圓的面積: " + area);*/
// 重寫findArea方法后
double area = cy.findArea();
System.out.println("圓柱的表面積: " + area);
System.out.println("========================");
// 體現(xiàn)子類默認調(diào)用父類空參構造器:super()
Cylinder cy1 = new Cylinder(); // 輸出: 3.1415926...
double volume1 = cy1.findVolume();
System.out.println("圓柱的體積為: " + volume1);
}
}
public class Circle {
private double radius; // 半徑
public Circle(){
radius = 1.0;
}
public Circle(double radius){
this.radius = radius;
}
public void setRadius(double radius){
this.radius = radius;
}
public double getRadius(){
return this.radius;
}
// 計算面積
public double findArea(){
return Math.PI * radius * radius;
}
}
/*
* 如果把父類空參構造器注釋掉子類空參構造器報錯是因為子類構造器先執(zhí)行父類的空參構造器super();而父類沒有空參構造器
* 如果再把子類的空參構造器注釋掉,子類類名又會報錯,因為,子類有系統(tǒng)默認提供的空參構造器,構造器還是要調(diào)用父類的空參構造器super();而父類沒有空參構造器
*/
public class Cylinder extends Circle{
private double length;
public Cylinder(){
// 實際默認有個super(); // 通過子類構造器造對象,他一上來就奔到父類空參構造器初始化半徑
length = 1.0;
}
public void setLength(double length){
this.length = length;
}
public double getLength(){
return this.length;
}
// 返回圓柱體積
public double findVolume(){
// return Math.PI * getRadius() * getRadius() * length;
// return findArea() * getLength(); // 子類把父類方法重寫覆蓋了,現(xiàn)在調(diào)用的是子類的findArea方法,所以不能用這個方法算
return super.findArea() * getLength(); // 調(diào)用父類的findArea方法
}
// 返回圓柱表面積
public double findArea(){
return Math.PI * getRadius() * getRadius() * 2 + Math.PI * getRadius() * 2 * getLength();
}
}
5.java.lang.Object類的理解
特別聲明是java.lang包下的Object類因為可能自己會創(chuàng)建同名的Object類
1.如果我們沒有先是的聲明一個類中的父類的話,則此類繼承于java.lang.Object類
2.所有的java類(除java.lang.Object類之外)都直接或間接的繼承于java.lang.Object類
3.所有java類具有java.lang.Object類聲明的功能
5.2.方法重寫的理解(override/overwrite)
方法重寫(override/ overwrite): 方法名形參相同,只改變方法體.權限范圍可相同或更大(除private外),返回值類型相同或更大
1.重寫: 子類繼承父類后,可以對父類中同名同參數(shù)的方法,進行覆蓋操作,執(zhí)行的就是自己的
2.應用: 重寫以后,當創(chuàng)建子類對象以后,通過子類對象調(diào)用子父類中的同名同參數(shù)的方法時,實際執(zhí)行的是子類重寫父類的方法(相當于子類自己定義的)
3.應用: 重寫規(guī)定:
方法的聲明: 權限修飾符 返回值類型 方法名(形參列表){
方法體
}
約定俗稱: 子類中的叫重寫的方法,父類中的叫被重寫的方法
(1)子類重寫的方法的方法名和形參列表與父類被重寫的方法的方法名和形參列表相同
(2)子類重寫的方法的權限修飾符不小于父類被重寫的方法的權限修飾符
特殊情況: 子類不能重寫父類中聲明為private權限的方法
(3)返回值類型:
父類被重寫的方法的返回值類型是void,則子類重寫的方法的返回值類型只能是void
父類被重寫的方法的返回值類型是A類型,則子類重寫的方法的返回值類型可以是A類或A類的子類
父類被重寫的方法的返回值類型是基本數(shù)據(jù)類型(如:double),則子類重寫的方法的返回值類型是必須相同的基本數(shù)據(jù)類型(必須也是double)(自動類型提升不等同于子父類關系)
(4)子類重寫的方法拋出的異常類型不大于父類被重寫的方法拋出的異常類型,(具體到異常處理時候講)
========================================================================
子類和父類中的同名同參數(shù)的方法要么都聲明為非static的(才考慮重寫),要么都聲明為static的(不是重寫),因為靜態(tài)方法不能被覆蓋,是隨著類的加載而加載的
面試題: 區(qū)分方法的重載和重寫
public class PersonTest {
public static void main(String[] args) {
Student s = new Student("計算機科學與技術");
s.eat();
s.walk(10); // 顯然這里walk方法里調(diào)的是父類的show方法,如果是重寫,子類的show方法會把父類的覆蓋
System.out.println("============");
s.study();
Person p1 = new Person();
p1.eat(); // 父類的對象調(diào)用父類的方法,顯示的還是父類的,自己定義就調(diào)自己
}
}
public class Person {
String name;
int age;
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃飯");
}
public void walk(int distance){
System.out.println("走路的距離是 " + distance + " 公里");
show();
eat();
}
// 父類中的聲明為private權限的方法不能被子類重寫
private void show(){
System.out.println("我是人");
}
// 父類被重寫的方法的返回值類型要大于等于子類重寫的方法
public Object info(){
return null;
}
public double info1(){
return 1.0;
}
}
public class Student extends Person{
String major;
public Student(){
}
public Student(String major){
this.major = major;
}
public void study(){
System.out.println("學習專業(yè)是: " + major);
}
// 對父類中eat()進行了重寫
public void eat(){
System.out.println("學生應都吃有營養(yǎng)的事物");
}
public void show(){
System.out.println("我是學生");
}
public String info(){
return null;
}
// 子類重寫方法的返回的基本數(shù)據(jù)類型必須和父類被重寫的相同
/*public int info1(){
return 1;
}*/
}
5.3.四種權限訪問修飾符
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
order.orderDefault = 1;
order.orderProtected = 2;
order.orderPublic = 3;
order.methodDefault();
order.methodProtected();
order.methodPublic();
// 同一個包中的其他類,不可以調(diào)用Order類中私有的屬性,方法
/*order.orderPrivate = 4;
order.methodPrivate();*/
}
}
public class Order {
private int orderPrivate;
int orderDefault;
protected int orderProtected;
public int orderPublic;
private void methodPrivate(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
void methodDefault(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
protected void methodProtected(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
public void methodPublic(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
}
==============================================
import com.oop.demo02.PermissionModifier.Order;
public class OrderTest {
public static void main(String[] args) {
// 通過造對象體現(xiàn)
Order order = new Order();
order.orderPublic = 1;
order.methodPublic();
// 不同包下的普通類(非子類)要使用Order類,不可以調(diào)用聲明為private,缺省,protected權限的屬性,方法
/*order.orderPrivate = 2;
order.orderDefault = 3;
order.orderProtected = 1;
order.methodPrivate();
order.methodDefault();
order.methodProtected();*/
}
// 通過不造對象體現(xiàn),但是調(diào)用(形參)實際還是要造對象
public void show(Order order){
order.orderPublic = 1;
order.methodPublic();
// 不同包下的普通類(非子類)要使用Order類,不可以調(diào)用聲明為private,缺省,protected權限的屬性,方法
/*order.orderPrivate = 2;
order.orderDefault = 3;
order.orderProtected = 1;
order.methodPrivate();
order.methodDefault();
order.methodProtected();*/
}
}
import com.oop.demo02.PermissionModifier.Order;
public class SubOrder extends Order {
public void method(){
// 因為已經(jīng)繼承了父類,相當于調(diào)用自己的屬性和方法,所以不用new對象也能調(diào)用
// 繼承后直接可以賦值,相當于繼承幫你聲明了
orderProtected = 1;
orderPublic = 2;
methodProtected();
methodPublic();
// 在不同包的子類中,不能調(diào)用父類Order類中聲明private和缺省權限的屬性,方法
/*orderDefault = 3;
orderPrivate = 2;
methodDefault();
methodPrivate();*/
}
}
5.4.super關鍵字的使用
1.super理解為:父類的
2.super可以用來調(diào)用(修飾):屬性,方法,構造器,調(diào)的父類(就近原則),this也一樣
3.super的使用
3.1.可以在子類類的方法或構造器中.通過使用"super.屬性"或"super.方法"的方式,顯式的調(diào)用父類中聲明的屬性或方法.但是,通常情況下,習慣省略"super.",(在父類有,子類沒有屬性或方法的情況下)
3.2.特殊情況下,當子類和父類中定義了同名的屬性時,要想在子類中調(diào)用父類中聲明的屬性,則必須顯式的使用"super.屬性"的方式,表明調(diào)用的是父類中聲明的屬性.
3.3.特殊情況,當子類重寫了父類中的方法后,想在子類的方法中調(diào)用父類中被重寫的方法時,則必須顯式的使用"super.方法"的方式,表明調(diào)用的是父類中被重寫的方法.
4.super調(diào)用構造器
4.1.可以在子類的構造器中顯式的使用"super(形參列表)"的方式,調(diào)用父類中聲明的指定的構造器
4.2."super(形參列表)"的使用,必須聲明在子類構造器的首行!
4.3.在類的構造器中,針對"this(形參列表)"(表示調(diào)用本類中重載的其他構造器或"super(形參列表)"(表示父類中指定的構造器),只能二選一,不能同時出現(xiàn)
4.4.在構造器的首行,沒有顯式的聲明"this(形參列表)"或:super(形參列表)",則默認調(diào)用的是父類中空參構造器:super()
4.5.一個類的構造器可能有多個,構造器的首行不是this(形參列表),就是super(形參列表),只能二選一,不可能有其他情況
一個類可能有n個構造器,至少有一個用super方式調(diào)用了父類的構造器super();最多有n-1個使用了this(形參列表),剩下一個沒用的只能是用了super();
public class SuperTest {
public static void main(String[] args) {
Student s = new Student();
s.show();
s.study();
Student s1 = new Student("tom", 21, "IT");
s1.show();
System.out.println("=================");
Student s2 = new Student();
}
}
public class Creature {
int age = 1;
}
public class Person extends Creature {
String name;
int age;
int id = 1001; // 身份證號
public Person(){
System.out.println("我無處不在!");
}
public Person(String name){
this.name = name;
}
public Person(String name,int age){
this(name);
this.age = age;
}
public void eat(){
System.out.println("人: 吃飯");
}
public void walk(){
System.out.println("人: 睡覺");
}
}
public class Student extends Person{
String major;
int id = 1002; // 學號 屬性此時在內(nèi)存中有兩個id,屬性不像方法一樣會覆蓋掉父類的
public Student(){
super(); // 編譯器先執(zhí)行了父類的構造方法才執(zhí)行子類的操作
}
public Student(String major){
super(); // 子類中每個構造器都默認隱式的調(diào)用了父類的空參構造器,
this.major = major;
}
// super使用構造器
public Student(String name, int age, String major){
// (有繼承權無使用權)繼承是子類繼承父類的屬性和方法,但調(diào)用了private修飾的屬性和方法,子類不能直接訪問
super(name,age); // 調(diào)用父類指定參數(shù)的構造器
this.major = major;
}
@Override
public void eat() {
System.out.println("學生多吃有營養(yǎng)的東西");
}
public void study(){
System.out.println("學生學習知識");
eat(); // 默認都是this.eat(),調(diào)用重寫以后的方法,
super.eat(); // 調(diào)用父類的方法
walk(); // 調(diào)用子類中未重寫父類中的方法,方法前寫this,super都可以通常省略掉了,區(qū)別在this先在子類中找,找沒才去父類中找,super直接去父類(包括直接父類和間接父類)中找,直到找到為止
}
public void show(){
// this: 先在當前的Student類當中找看看有無該屬性的結構,如果在自己類里沒找到,他再接著去父類中找
// super: 不在(當前)子類找,直接去父類找
System.out.println("name = " + this.name + ", age = " + super.age);
System.out.println("id = " + id); // id前省略的是: this.
System.out.println("id = " + super.id);
}
}
5.5.子類對象實例化過程
子類對象實例化的全過程
如果沒有明確地指出父類,Object類就是該類的父類,是默認的,而且不用顯式地寫出來
1.從結果上來看:(繼承性)
子類繼承父類后,就獲取了父類聲明的屬性或方法
創(chuàng)建子類的對象,在堆空間中,就會加載所有父類(直接父類,間接父類)中聲明的屬性
2.從過程上來看:
當通過子類的構造器創(chuàng)建子類對象時,一定會直接或間接的調(diào)用其父類的構造器,進而調(diào)用父類的父類的構造器...
直到調(diào)用了java.lang.Object類中空參的構造器為止.正因為家在過所有的父類的結構,所以才可以看到內(nèi)存中有父類中的結構,子類對象才可以考慮進行調(diào)用
明確: 雖然創(chuàng)建子類對象時,調(diào)用了父類的構造器,但是自始至終就創(chuàng)建過一個對象,即為new的子類對象,因為堆中值分配了一個地址
- 練習: 繼承&super
public class AccountTest {
public static void main(String[] args) {
Account acct = new Account(1122, 20000, 0.045);
acct.withdraw(30000);
System.out.println("賬戶余額: " + acct.getBalance());
acct.withdraw(2500);
System.out.println("賬戶余額: " + acct.getBalance());
acct.deposit(3000);
System.out.println("賬戶余額: " + acct.getBalance());
System.out.println("月利率: " + (acct.getMonthInterest() * 100) + "%");
}
}
public class Account {
private int id; // 賬號
protected double balance; // 余額
private double annualInterestRate; // 年利率
// 子類構造器/類名報錯解決辦法: 1.給父類造空參構造器 2.給子類造指定構造器
public Account(int id, double balance, double annualInterestRate){
super();
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public void setId(int id){
this.id = id;
}
public int getId(){
return this.id;
}
public void setBalance(double balance){
this.balance = balance;
}
public double getBalance(){
return this.balance;
}
public void setAnnualInterestRate(double annualInterestRate){
this.annualInterestRate = annualInterestRate;
}
public double getAnnualInterestRate(){
return this.annualInterestRate;
}
// 返回月利率
public double getMonthInterest(){
return this.annualInterestRate / 12;
}
// 取錢
public void withdraw(double amount){
if (balance >= amount){
balance -= amount;
return;
}
System.out.println("余額不足");
}
// 存錢
public void deposit(double amount){
if (amount > 0){
balance += amount;
}
}
}
public class CheckAccount extends Account{
private double overdraft; // 可透支限額
public CheckAccount(int id, double balance, double annualInterestRate, double overdraft){
super(id,balance,annualInterestRate);
this.overdraft = overdraft;
}
public void setOverdraft(double overdraft){
this.overdraft = overdraft;
}
public double getOverdraft(){
return this.overdraft;
}
@Override
public void withdraw(double amount) {
// 余額足夠消費
if (this.getBalance() >= amount){
// 方式一:
// setBalance(getBalance() - amount);
// 方式二:
super.withdraw(amount); // super.屬性/方法可以調(diào)用父類的屬性和方法
// 方式三: 改balance的權限為protected
// balance = balance - amount;
// 透支+余額足夠消費
}else if (overdraft >= amount - getBalance()){
overdraft = overdraft - (amount - getBalance()); // 先從可透支余額取出
// setBalance(0); // 再把余額全清空
// 或
super.withdraw(getBalance());
}else{
System.out.println("超過可透支限額");
}
}
}
public class CheckAccountTest {
public static void main(String[] args) {
CheckAccount acct = new CheckAccount(1122, 20000, 0.045, 5000);
acct.withdraw(5000);
System.out.println("賬戶余額: " + acct.getBalance() + ", 可透支余額: " + acct.getOverdraft());
acct.withdraw(18000);
System.out.println("賬戶余額: " + acct.getBalance() + ", 可透支余額: " + acct.getOverdraft());
acct.withdraw(3000);
System.out.println("賬戶余額: " + acct.getBalance() + ", 可透支余額: " + acct.getOverdraft());
}
}
5.6.多態(tài)性的使用
面向?qū)ο筇卣髦? 多態(tài)性
1.理解多態(tài)性: 可以理解為一個事物的多種形態(tài)
2.何為多態(tài)性: 父類的引用指向子類的對象(或子類的對象賦給父類的引用)
3.多態(tài)的使用: 虛擬方法使用
有了對象的多態(tài)性后,在編譯期,只能調(diào)用父類中聲明的方法,但在運行期,實際執(zhí)行的是子類重寫父類的方法
如果子類中沒有重寫父類的方法,就執(zhí)行父類的方法(這樣使用多態(tài)性沒有意義)
總結:編譯看等號左邊的類型,運行看右邊的對象(編譯時是父類,執(zhí)行時是子類)
4.多態(tài)性的使用前提(只說方法的事沒有屬性的事,多態(tài)性方面跟屬性沒關系): (1)要有類的繼承關系 (2)方法的重寫
5.對象的多態(tài)性: 只適用于重寫的方法,不適用于屬性(屬性的編譯和運行都看左邊).
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man man = new Man();
man.eat();
man.age = 25;
man.earnMoney();
System.out.println("*********************");
// 對象的多態(tài)性: 父類的引用(p2)指向子類的對象(new Man())
Person p2 = new Man(); // 多態(tài)的形式
Person p3 = new Woman();
// 多態(tài)的使用: 當調(diào)用子類同名同參數(shù)的方法時,實際執(zhí)行的是子類重寫父類的方法---虛擬方法調(diào)用
// 有了多態(tài)的形式后,通過引用調(diào)用子父類都聲明過的方法,真正執(zhí)行的是子類重寫父類的方法
p2.eat(); // 編譯看左,運行看右
p2.walk();
// p2.earnMoney(); // 多態(tài)性的使用不能調(diào)父類沒有的方法
System.out.println(p2.id);
}
}
public class Person {
String name;
int age;
int id = 1001;
public void eat(){
System.out.println("人吃飯");
}
public void walk(){
System.out.println("人走路");
}
}
public class Man extends Person{
boolean isSmoking;
int id = 1002;
public void earnMoney(){
System.out.println("男人負責掙錢");
}
/*@Override
public void eat() {
System.out.println("男人多吃肉,長肌肉");
}*/
@Override
public void walk() {
System.out.println("男人霸氣的走路");
}
}
public class Woman extends Person{
boolean isBeauty;
public void goShopping(){
System.out.println("女人喜歡購物");
}
@Override
public void eat() {
System.out.println("女人少吃為了減肥");
}
@Override
public void walk() {
System.out.println("女人窈窕的走路");
}
}
- 多態(tài)性的使用舉例:
import java.sql.Connection;
// 多態(tài)性的使用舉例一:
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.func(new Dog());
test.func(new Cat());
}
// 使用多態(tài)性: 凡是new子類的對象
// 每種動物對一件事表現(xiàn)出來的形態(tài)都不一樣 就是多態(tài)
public void func(Animal animal){ // Animal animal = new Dog(); 對象多態(tài)性的形式
// 因為形參是Animal類型的,所以只能調(diào)Animal類里的方法,但實際new了一個Dog類的對象,真正運行的時候,是Dog重寫父類方法的執(zhí)行
animal.eat();
animal.shout();
}
// 不用多態(tài)性的寫法: 聲明什么類型只能new這個類型的對象
public void func(Dog dog){
dog.eat();
dog.shout();
}
public void func(Cat cat){
cat.eat();
cat.shout();
}
}
// 父類
class Animal{
public void eat(){
System.out.println("動物進食");
}
public void shout(){
System.out.println("動物叫");
}
}
// 子類
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨頭");
}
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("貓吃魚");
}
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
// 舉例二:
class Order{
public void method(Object obj){
}
}
// 舉例三:
class Driver{
// 形參里傳哪個對象就建立跟哪個數(shù)據(jù)庫的連接,因為子類方法都重寫過了,自然就能實現(xiàn)對那個數(shù)據(jù)庫中表的操作
public void doData(Connection conn){ // conn = new MySQLConnection();/ conn = new OracleConnection();/...
// 規(guī)范步驟去操作數(shù)據(jù) 都是子類重寫過的方法
/*conn.method1();
conn.method2();
conn.method3();*/
}
}
Java面向?qū)ο髮W習三條主線:
1.Java類及類的成員:屬性,方法,構造器秉扑;代碼塊,內(nèi)部類
2.面向?qū)ο蟮娜筇卣鳎悍庋b性晴叨,繼承性,多態(tài)性矾屯,(抽象性)
3.其他關鍵字:this兼蕊,super,static件蚕,final孙技,abstract产禾,interface,package牵啦,import
(類,對象; 面向?qū)ο蟮娜筇卣?...)
類: 是對一類事物的描述, 抽象的, 概念上的內(nèi)容
對象: 實實在在存在的一個個體. new出來的東西,在內(nèi)存當中存在的, 在內(nèi)存中真正的創(chuàng)建了一個對象,占據(jù)了內(nèi)存一定空間
對象: 是由類派生(new)出來的
面向?qū)ο缶幊?Objected-Oriented-Programming,OOP),Java的核心思想
面向?qū)ο缶幊痰谋举|(zhì)是: 以類的方式組織代碼,以對象的形式組織(封裝)數(shù)據(jù).
抽象 把相似的抽取出來
-
三大特性:
- 封裝 把一個東西打包封裝,留一個口方便把東西拿出
- 繼承 子類可以繼承父類的所有東西,多個子類繼承的東西是一樣的
- 多態(tài) 同一個事物會有多種形態(tài)
從認識論角度考慮是先有對象后有類. 對象,是具體的事物. 類,是抽象的, 是對對象的抽象
從代碼運行角度考慮是先有類后有對象. 類是對象的模板.
值傳遞
// 值傳遞
public static void main(String[] args){
int i = 1;
System.out.println(i);
Demo04.change(i);
System.out.println(i);
i = Demo04.change1(i);
System.out.println(i);
}
// 返回值為空
public static void change(int a){
a = 10;
}
// 返回值不為空
public static int change1(int a){
a = 10;
return a;
}
- 引用傳遞
public class Demo05 {
// 一個類可以有多個public class 但是可以有多個class
// 引用傳遞: 對象,本質(zhì)還是值傳遞
public static void main(String[] args) {
// Person是一個引用亚情,指向的是堆里面的對象
Person person = new Person(); // 實例化Person對象
System.out.println(person.name); // null
// Demo05.change(person);
change(person); // 引用傳遞傳遞的是對象的地址
System.out.println(person.name);// dijia
// 變量名對應的內(nèi)存地址不一樣
// 值傳遞,傳遞后的值被改了不影響原變量
// 因此只是將方法中的變量指向了字符串,并未改變main方法原變量的引用
}
public static void change(Person person) {
// person是一個對象:指向Person這個類
// 或是 Person person = new Person();這是個具體的人,可以改變屬性!
person.name = "dijia";
}
}
// 定義一個Person類,有一個屬性: name
class Person {
String name;
}
4.2.創(chuàng)建與初始化對象
- 面向?qū)ο蟮膬蓚€要素:
- 類:對一類事物的描述,是抽象的哈雏,概念上的定義
- 對象:是實際存在的該類事物的每個個體楞件,因而也稱為實例(instance)
- 類和對象的創(chuàng)建
面向?qū)ο笏枷氲捏w現(xiàn)一 ,類和對象的創(chuàng)建和執(zhí)行操作(面向?qū)ο笏枷肼涞氐貙崿F(xiàn)):
1.創(chuàng)建類,設計類的成員
2.創(chuàng)建類的對象
3.通過"對象.屬性"或"對象.方法()"調(diào)用對象的結構
二,如果創(chuàng)建了一個類的多個對象,每個對象都獨立的擁有一套類的屬性(無static關鍵字的)
意味著,如果我們修改一個對象的屬性a,不影響另一個屬性a的值.(static為可共享屬性)
// 測試類
public class PersonTest {
// main方法作為程序入口
public static void main(String[] args) {
// 創(chuàng)建Person類的對象=類的實例化=實例化類
Person p1 = new Person(); // 類是對象的類型,對象是類的實例,Person是引用的變量類型
// 調(diào)用對象的(功能和行為)結構: 屬性和方法
// 調(diào)用屬性: "對象.屬性;"
p1.name = "tom";
p1.isMale = true;
System.out.println(p1.name);
// 調(diào)用方法: "對象.方法();"
p1.eat();
p1.sleep();
p1.talk("Chinese");
System.out.println("================");
Person p2 = new Person();
System.out.println(p2.name); // null
System.out.println(p2.isMale); // false
System.out.println("===================");
//
Person p3 = p1; // p1對象的地址值賦給了p3,導致p1和p3指向了堆空間中的同一個對象實體
System.out.println(p3.name); // tom
p3.age = 10;
System.out.println(p1);
System.out.println(p3);
System.out.println(p1.age); // 10
}
}
// 創(chuàng)建類,設計類的成員:包括成員方法,成員變量
class Person{
// 屬性= 成員變量= 域,字段
String name;
int age = 1;
boolean isMale;
// 設計方法:行為
public void eat(){
System.out.println("人可以吃飯");
}
public void sleep(){
System.out.println("人可以睡覺");
}
public void talk(String language){
System.out.println("人可以說話使用:" + language);
}
- 對象的內(nèi)存解析:
- 棧的特點:先進后出
1.new Person(); 先造了一個對象,new的結構都在堆里,意味著在堆空間中要造一個對象的實體,該對象會有個地址值稱作首地址值; 在棧空間中聲明了一個變量p1,此時的p1實際是定義在main方法中的,方法中定義的變量都是局部變量,此時,new結構的數(shù)據(jù)賦值給p1,其實賦過來的是個地址,通過賦值地址值后,椛驯瘢空間中的p1就指向了堆空間中造的對象實體,類在設計的時候聲明過三個屬性,這些屬性不同于局部變量,屬性存在于堆空間中,在造好的對象里面,在賦值過程中會給定默認的初始化值和先前賦好的值
2.通過p1找到new的結構調(diào)用name把null改成tom,tom等賦值后的數(shù)據(jù)實際在方法區(qū)中的字符串常量池里
3.重新在堆空間中造一個對象實體, 該實體有一個首地址值,將這個地址值賦值給椡两空間中現(xiàn)在加載的變量p2,棧空間中的p2對象就可以指向堆空間的這個對象實體,對象實體也獨立生成一份默認初始化值和先有的值
4.又在main方法(楉镉蓿空間)中聲明了一個p3局部變量,p3拿p1賦的值,p1存的是地址值,所以傳給p3也是相同地址值,通過p1傳過來的地址值,p3也順著地址指向p1的在堆空間中的結構,所以p3不能叫新創(chuàng)建的一個對象,只能算新聲明的一個變量,此時p1和p3指向了堆空間中的同一個對象實體
4.3.屬性與局部變量的對比
類中屬性的使用
屬性(成員變量) vs 局部變量
- 1.相同點:
- 1.1.定義變量的格式: 數(shù)據(jù)類型 變量名 = 變量值
- 1.2.先聲明,后使用
- 1.3.變量都有其對應的作用域
- 2.不同點:
- 2.1.在類中聲明的位置不同
屬性:直接定義在類的一對{}里
局部變量:聲明在方法內(nèi),方法形參,代碼塊內(nèi),構造器形參,構造器內(nèi)部的變量 - 2.2.關于權限修飾符的不同
屬性:可以在聲明屬性時,指明其權限,使用權限修飾符.
常用的權限修飾符: private,public,缺省(相當于默認,default),protected --->類的封裝性
目前,聲明屬性時,使用缺省就行了
局部變量:不可以使用權限修飾符,可理解為其權限被方法的權限代替了 - 2.3.默認初始化值的情況:
屬性: 類的屬性,根據(jù)其類型,都有默認初始化值.
整型(byte,short,int,long): 0
浮點型(float,double): 0.0
字符型(char), 0 (或'\u0000')
布爾型(boolean): false
引用數(shù)據(jù)類型:(類(特殊的String),數(shù)組,接口): null
局部變量: 沒有初始化值,意味著調(diào)用局部變量之前,一定要顯示賦值
特別地: 形參在調(diào)用時,賦值即可. - 2.4.二者在內(nèi)存中加載的位置:
(非static)屬性:加載到堆空間中(static屬性都放在方法區(qū)中)
局部變量:加載到椪て空間中
- 2.1.在類中聲明的位置不同
public static void main(String[] args) {
User u1 = new User();
System.out.println(u1.name);
System.out.println(u1.age);
System.out.println(u1.isMale);
u1.talk("中文");
u1.eat();
}
}
class User{
// 屬性(成員變量)
String name; // private權限小,出了定義的這個類就不能調(diào)了
public int age; // public權限大,在外的就能調(diào)
boolean isMale;
public void talk(String language){ // language:形參(局部變量)
System.out.println("我們使用" + language + "交流");
}
public void eat(){
String food = "烙餅"; // (聲明)定義在方法內(nèi)的變量叫局部變量
System.out.println("北方人吃" + food);
}
4.4.空指針異常(NullPointerException)
關于垃圾回收器: GC
在Java語言中,垃圾回收器主要針對的是堆內(nèi)存.
當一個Java對象沒有任何引用指向該對象的時候,
GC會考慮將該垃圾數(shù)據(jù)釋放回收掉.
- 出現(xiàn)空指針異常的前提條件是?
- "空引用"訪問實例(對象相關,例如id)相關的數(shù)據(jù)時,都會出現(xiàn)呢空指針異常.
/*
空指針異常
*/
public class NullPointerTest {
public static void main(String[] args) {
// 創(chuàng)建客戶對象
Customer1 c1 = new Customer1();
// 訪問這個客戶對象
System.out.println(c1.id);
// 重新給id賦值
c1.id = 242;
System.out.println(c1.id);
c1 = null;
// NullPointerException 空指針異常
// 編譯器沒問題,因為編譯器只檢查語法,編譯器發(fā)現(xiàn)c是Customer類型,
// Customer類型中由id屬性,所以可以: 調(diào)用c.id,語法過了,
// 但是運行的時候需要對象的存在,但是對象丟失,就只能出現(xiàn)異常
System.out.println(c1.id);
}
}
// 客戶類
class Customer1{
// 客戶id屬性
int id; // 成員變量中的實例變量,應該先創(chuàng)建對象,再通過"引用."的方式訪問
}
4.5.對象數(shù)組
public class StudentTest1 {
public static void main(String[] args) {
// 聲明自定義類Student類型的對象數(shù)組
Student1[] stus = new Student1[20]; // 類似于String[] arr = new String[];
for (int i = 0;i < stus.length;i++){
// 給數(shù)組元素賦值
stus[i] = new Student1();
// 給Student對象的屬性賦值
stus[i].number = (i + 1);
// 年級: [1,6]
// 隨機數(shù)a~b公式: int value = (int)(Math.random() * (b - a + 1) - a)
stus[i].state = (int)(Math.random() * (6 - 1 + 1) + 1);
// 分數(shù): [0~100]
stus[i].score = (int)(Math.random() * (100 - 0 + 1));
}
// 想在main方法里調(diào)用其他方法,要在main方法公共類里造一個當前類的對象,
StudentTest1 test = new StudentTest1();
// 遍歷學生數(shù)組
test.print(stus);
/*for (int i = 0;i < stus.length;i++){
// stus[i].info();
System.out.println(stus[i].info2());
}*/
System.out.println("======================");
// 打印三年級(state值為3)的學生信息
test.searchState(stus,3);
/*for (int i = 0;i < stus.length;i++){
if (stus[i].state == 3){
stus[i].info();
}
}*/
System.out.println("========================");
// 使用冒泡排序按學生成績排序,并遍歷所有學生信息
/* for (int i = 0;i < stus.length - 1;i++){
for (int j = 0;j < stus.length - 1 - i;j++){
if (stus[j].score > stus[j+1].score){
// 這里要交換數(shù)組元素的對象!意思是把整個人交換
// 如果只交換成績,學號和年級都沒換,相當于"作弊"
Student1 temp = stus[j];
stus[j] = stus[j+1];
stus[j+1] = temp;
}
}
}*/
/*for (int i = 0;i < stus.length;i++){
System.out.println("學號:" + stus[i].number + "年級:" + stus[i].state + "成績:"+ stus[i].score);
}*/
}
// 遍歷Student1[]類型數(shù)組的操作
/**
* @Description 遍歷Student1[]類型數(shù)組的操作
* @author tiga
* @date time
* @param stus
*/
public void print(Student1[] stus){ // 傳入Student1[]類型數(shù)組的形參
for (int i = 0;i < stus.length;i++){
stus[i].info();
}
}
// 查找某年級的學生信息
/**
*
* @Description 查找Student類型數(shù)組中指定年級的學生信息
* @author tiga
* @date time
* @param stus 要查找的數(shù)組
* @param state 要查找的年級
*/
public void searchState(Student1[] stus,int state){ // 傳入要找的那個數(shù)組和要找的年級為形參
for (int i = 0;i < stus.length;i++){
if (stus[i].state == state){
System.out.println(stus[i].info2());
}
}
}
// 冒泡排序?qū)W生成績,遍歷學生信息
public void sort(Student[] stus){
for (int i = 0;i < stus.length - 1;i++){
for (int j = 0;j < stus.length - 1 - i;j++){
if (stus[j].score > stus[j+1].score){
Student temp = stus[j];
stus[j] = stus[j+1];
stus[j+1] = temp;
}
}
}
}
}
class Student1{
int number; // 學號
int state; // 年級
int score; // 成績
public void info(){
System.out.println("學號:" + number + "年級:" + state + "成績:" + score);
}
public String info2(){
return "學號:" + number + "年級:" + state + "成績:" + score;
}
}
- 對象數(shù)組的內(nèi)存解析
引用類型變量只能存儲null或?qū)ο蟮牡刂分?含變量的類型).
4.6.匿名對象
一.理解"萬事萬物皆對象"
1.在Java語言范疇中,都將功能,結構等封裝到類中,通過類的實例化,來調(diào)用具體的功能結構
Scanner,String
文件:File
網(wǎng)絡資源: URL
2.涉及到Java語言與前端HTML,后端的數(shù)據(jù)庫交互時,前后端的結構在Java層面交互時,都體現(xiàn)為類,對象
二.內(nèi)存解析的說明:
1.引用類型的變量,只可能存儲兩類值:null 或 地址值(含變量的類型)
三.匿名對象的使用
1.理解:創(chuàng)建的對象,沒有顯式的賦給一個變量名.即為匿名對象
2.特征: 匿名對象只能調(diào)用一次
3.使用: 可以將匿名對象放入方法里多次調(diào)用
- 內(nèi)存簡單分析
在調(diào)用方法里new完Phone這個類的對象,在堆里產(chǎn)生的空間也會有一個地址值,調(diào)用時new的對象其實是賦值給了定義類里的方法里作為形參,形參又是個局部變量,且該變量會存入棧中,相當于把new Phone這個類出來的對象的地址值賦給了形參,只要是賦給了變量,對象就可以多次使用,從而形參就指向了堆中new的新對象, 就能通過新對象的地址調(diào)用其他方法, 這時調(diào)用的是同一個匿名對象,這相當于把匿名的對象賦給了有名的對象(形參).
public class InstanceTest {
public static void main(String[] args) {
Phone p = new Phone(); // p就是Phone對象的變量名
// p = null;
System.out.println(p);
p.sentEmail();
p.playGame();
// 匿名對象
// 兩者調(diào)用的不是同一對象
new Phone().sentEmail();
new Phone().playGame();
new Phone().price = 2000;
new Phone().showPrice(); // 0.0
System.out.println("======================");
// 匿名對象的使用
PhoneMall mall = new PhoneMall();
mall.show(new Phone()); // 理論上是匿名
ShowAddress add = new ShowAddress();
add.printAdd(new PhoneMall());
}
}
class ShowAddress{
public void printAdd(PhoneMall phone){
System.out.println(phone);
}
}
class PhoneMall{
public void show(Phone phone){ // 實際上形參就是匿名函數(shù)的名字
phone.sentEmail();
phone.playGame();
}
}
class Phone{
double price; // 價格
public void showPrice(){
System.out.println("手機價格是:" + price);
}
public void sentEmail(){
System.out.println("發(fā)送郵件");
}
public void playGame(){
System.out.println("玩游戲");
}
}
- 理解變量的賦值
- 當在方法中定義了基本數(shù)據(jù)類型的局部變量,其變量存放在棧中,給變量賦值的數(shù)字就是變量在棧內(nèi)存中存放的東西, 變量之間的賦值傳遞, 也是傳遞數(shù)字.
int m = 10;
int n = m; // n == 10
- 當在方法中定義了引用類型的局部變量,其變量也是存放在棧中,但是變量賦給的值的地址值, 所以當同類型變量之間賦值傳遞, 是傳遞地址,而不是數(shù)字.
- 值傳遞機制: 針對基本數(shù)據(jù)類型
一.方法的形參的傳遞機制: 值傳遞
1.形參: 方法定義時,聲明的小括號內(nèi)的參數(shù)
2.實參,方法調(diào)用時,實際傳遞給形參的數(shù)據(jù),可以是具體數(shù)也可以是變量
二.值傳遞機制: 如果參數(shù)是基本數(shù)據(jù)類型,此時實參賦給形參的是實參真實存儲的數(shù)據(jù)值.
- 內(nèi)存分析.
1.m和n沒有換成的原因: 在棧中,先從main方法進來,main方法中定義的m, n分別賦值10, 20;
2.通過對象v調(diào)swap方法,v就不往里面寫了,當調(diào)用swap方法時候,將上面實參m, n表示數(shù)據(jù)的值,賦給了下面swap方法的形參m, n(新定義的),這兩個形參還要在棧中提供,形參m, n分別是實參賦值過來的,也是10和20,此時的m, n 是swap方法中的;
3.接著v調(diào)用swap方法就進入方法執(zhí)行,一進去又聲明新變量temp,temp拿了形參m賦的值,接著形參n的值給了m,接著又把temp給了n,最swap方法實現(xiàn)效果是把swap方法里的m和n交換,如果在方法里面輸出,m和n就會交換,但是如果方法內(nèi)不輸出,swap方法執(zhí)行完以后就銷毀了(出棧), 銷毀之后接著在main方法下面再輸出,輸出的m和n的值是main方法賦的值,因為swap方法出棧以后,輸出的m和n保存的值是main里面定義的m和n兩變量的值, main里面定義的變量在main的整個大括號區(qū)間里都是有效的,所以main里面輸出的還是main里定義的值,所以兩數(shù)沒換成. 數(shù)組元素的值交換也是同理
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println(m + "," + n);
ValueTransferTest1 test = new ValueTransferTest1();
test.swap(m,n); // 實參
/*int temp = m;
m = n;
n = temp;*/
System.out.println(m + "," + n); // 10, 20
}
public void swap(int m,int n){ // 新變量實參傳入的形參
int temp = m;
m = n;
n = temp;
// System.out.println(m + "," + n);
}
值傳遞機制: 針對引用數(shù)據(jù)類型
靜態(tài)方法調(diào)用非靜態(tài)方法要先new一個對象
靜態(tài)方法調(diào)用靜態(tài)方法直接調(diào)用
非靜態(tài)方法調(diào)用非靜態(tài)方法直接調(diào)用內(nèi)存分析
1.從main方法進來,在棧中new了一個data變量,也在堆空間中new了一個對象,該對象會有個地址值,并把地址值賦值給椪竟剩空間的data變量,通過地址值,椊耘拢空間中的data變量指向堆空間中的對象實體
2.接著下一步,data(對象)變量調(diào)用Data類中定義的變量m, n,一開始m, n的初始值都為0,通過對象調(diào)屬性改了m, n的值,此時輸出就是10, 20.
3.接著新建的對象去調(diào)swap方法,順便把引用類型data傳入作為形參,傳入的引用類型data是上面的實參,所以形參是引用類型就存的是地址值,形參在棧中加載,相當于把main方法中data的地址值復制了一份,形參有了地址值后,他就指向堆空間中同一對象實體
4.進入swap方法體,通過data(形參)變量調(diào)用m的值,賦給一個swap方法內(nèi)部聲明的局部變量temp,temp也加載在棧中,data的n的值賦給data的m,temp的值又賦給了data的n,至此方法執(zhí)行結束,結束后,swap方法定義的temp和data變量就出棧了,出棧后形參data的指針就沒了,但是堆中對象還在,還有實參data的指向,這樣判斷堆中對象就不是垃圾,不能回收了.
5.回到main方法接著執(zhí)行輸出 data.m和data.n, 這兩data變量是實參data, m和n的值就交換了
- 練習: 方法的參數(shù)傳遞
4.7.面向?qū)ο蟮奶卣饕? 封裝和隱藏
面向?qū)ο蟮奶卣饕? 封裝和隱藏
一.問題的引入:
當我們創(chuàng)建一個類的對象以后,我們可以通過"對象.屬性"的方式,對對象的屬性進行賦值,這里,賦值操作要瘦到屬性的數(shù)據(jù)類型和存儲范圍的制約
除此之外,沒有其他制約條件,但是在實際問題中,我們往往需要給屬性賦值加入額外的限制調(diào)價.這個特條件就不能在屬性聲明時體現(xiàn),我們只能通過方法進行限制條件的添加.(比如: setLegs())
同時,我們需要避免用戶再使用"對象.屬性"的方式對屬性進行賦值.則需要將屬性聲明為私有的(private)
-->此時,針對于屬性就體現(xiàn)了封裝性
二.封裝性的體現(xiàn)之一(不等同于封裝性):
將類的屬性xxx私有化(private),同時,提供公共的(public)方法來獲取(getXxx)和設置(setXxx)此屬性的值
拓展: 封裝性的體現(xiàn): 1.如上 2.不對外暴露的私有的方法 3.單例模式: 把構造器私有化
三.封裝性的體現(xiàn),需要權限修飾符來配合
1.Java規(guī)定的4種權限(從小到大排列): private,缺省,protected,public
2.4種權限可以用來修飾類及類的內(nèi)部結構: 屬性,方法,構造器,內(nèi)部類,(代碼塊不行)
3.具體的,4中權限都可以用來修飾類的內(nèi)部結構:屬性,方法,構造器,內(nèi)部類
修飾類的話,只能用: 缺省,public,不能用private
總結封裝性: Java提供了4中權限修飾符來修飾類及類的內(nèi)部結構,體現(xiàn)類及類的內(nèi)部結構在被調(diào)用時的可見性的大小
體現(xiàn)一: 將類的屬性xxx私有化(private),同時,提供公共的(public)方法來獲取(getXxx)和設置(setXxx)此屬性的值
體現(xiàn)二: 不對外暴露的私有的方法
體現(xiàn)三:單例模式(將構造器私有化,外邊不能隨便調(diào)構造器, 里面自己只造一個對象,大家都只拿一個對象用)
體現(xiàn)四: 如果不希望類在包外被調(diào)用,可以將類設置為缺省的.
public class AnimalTest {
public static void main(String[] args) {
Animal a = new Animal();
a.name = "大黃";
// a.age = 1; // 將屬性私有化后不能直接調(diào)用,要通過public方法調(diào)用
// a.legs = 4; // 作用域不可見 The field Animal.legs is not visible
a.show(); // 誰調(diào)的show方法,方法里顯示的屬性值就是誰的
a.setLegs(-6);
// a.legs = -4; // The field Animal.legs is not visible
a.show();
}
}
class Animal{
String name;
private int age;
// private好處:setLegs提供方法讓legs賦值并加入判斷條件,還能把直接調(diào)屬性給禁掉
// 此時就能達到對 legs屬性的封裝,相當于這個屬性沒有對外暴露
private int legs; // 腿的數(shù)量 限制為私有權先,可見但類外面不能調(diào)用
public void setLegs (int l){
if (l >= 0 && l % 2 == 0){
legs = l;
}else{
legs = 0;
// 另一種寫法,拋出一個異常(暫時沒講)
}
}
public int getLges(){
return legs;
}
public void eat(){
System.out.println("動物進食");
}
public void show (){
System.out.println("name = " + name + ",age = " + age + ",legs = " + legs);
}
// 提供關于屬性age的get和set方法
public int getAge(){
return age;
}
public void setAge(int a){
age = a;
}
}
- 練習: 封裝性的基本使用
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
// p1.age = 1; // age私有化后不能直接調(diào)用,編譯不通過
p1.setAge(12);
System.out.println("年齡為:" + p1.getAge());
}
}
public class Person {
private int age;
public void setAge(int a){
if (a < 0 || a > 130){
// throw new RuntimeException("傳入數(shù)據(jù)非法"); // [[拋異常
System.out.println("傳入數(shù)據(jù)非法");
return; // 結束方法
}
age = a;
}
public int getAge(){
return age;
}
// 絕對不要把設置和獲取的方法合起來寫! 這些寫方法沒意義
/*public int doAge(int a){
age = a;
return a;
}*/
}
4.8.類的成員之三: 構造器(或構造方法)
- 類的結構之三,構造器(或構造函數(shù)(c++)或構造方法但又不同于方法,constructor)的使用
construct: 建設,建造. construction constructor: 建設者- 一.構造器的作用:
1.創(chuàng)建對象
2.初始化對象的屬性- 二.說明:
1.如果沒有顯式的定義類的構造器的話,則系統(tǒng)默認提供默認無參構造器
2.定義構造器的格式(不同于方法): 權限修飾符 類名(形參列表){}
3.一個類中定義多個構造器,彼此構成重載
4.一旦我們顯式的定義了類的構造器之后,系統(tǒng)就不在提供默認的無(空)參構造器(可用國家貧困補貼舉例)
5.一個類中,至少會有一個構造器.(相當于給我們造對象的能力,有構造器才能造對象)(可能是默認的也可是自己寫的)
public class PersonTest {
public static void main(String[] args) {
// 創(chuàng)建類的對象(構造器的使用): new + 構造器 (構造器和類同名)
Person p = new Person(); // 自己定義了顯式的構造器
p.eat();
Person p1 = new Person("tom");
System.out.println(p1.name);
}
}
class Person{
int age;
String name;
// 構造器
public Person(){
// 可以用于初始化 比如實例化這個類時就需要傳值,而不是在調(diào)用方法才傳值
// 構造器里的代碼在new對象的時候就執(zhí)行了
System.out.println("Person().....");
}
// 構造器的重載
public Person(String n){ // 形參的作用 初始化造的對象當前的屬性
name = n; // name: 當前正在創(chuàng)建的對象的屬性
}
public Person(String n, int a){
name = n;
age = a;
}
public void eat(){
System.out.println("人吃飯");
}
public void study(){
System.out.println("人睡覺");
}
}
- 練習: 構造器的基本使用
1.創(chuàng)建程序,在其中定義兩個類:Person和PersonTest類.定義如下:
用setAge()設置熱的合法年齡(0~130),用getAge()返回人的年齡
2.1.在前面定義的Person類中添加構造器,利用構造器設置所有人的age屬性初始值都為18
2.2.修改上題中類和構造器,增加name屬性,使得每次創(chuàng)建Person對象的同時初始化對象的age屬性值和name屬性值
3.如果類中提供了至少一個構造器,但是沒有提供空參構造器,那么構造對象時如果不提供參數(shù)就是不合法的.也就是創(chuàng)建沒有形參的對象會報錯
/*
* 在PersonTest類中實例化Person類的對象b
* 調(diào)用setAge()和getAge()方法,體會Java的封裝性
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
// p1.age = 1; // age私有化后不能直接調(diào)用,編譯不通過
p1.setAge(12);
// p1.getAge(); // age == 18
System.out.println("年齡為:" + p1.getAge());
Person p2 = new Person("tom", 21);
// 屬性私有化后只能通過方法來調(diào)用
System.out.println("name: " + p2.getName() + ", age: " + p2.getAge());
}
}
public class Person {
// 代碼順序: 屬性-->構造器-->方法
private int age;
private String name;
public Person(){
age = 18;
}
// 體現(xiàn)了有參構造器
public Person(String n, int a){
name = n;
age = a;
}
public void setAge(int a){
if (a < 0 || a > 130){
// throw new RuntimeException("傳入數(shù)據(jù)非法"); // 拋異常
System.out.println("傳入數(shù)據(jù)非法");
return; // 結束方法
}
age = a;
}
public int getAge(){
return age;
}
// 體現(xiàn)了封裝性
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
// (錯誤寫法)絕對不要把設置和獲取的方法合起來寫! 這些寫方法沒意義
/*public int doAge(int a){
age = a;
return a;
}*/
}
- 構造器練習: 三角形
/*
* 編寫兩個類,TriAngle和TriAngleTest,其中TriAngle類中聲明私有的底邊長base和高height,同時聲明公共方法訪問私有變量.
* 此處,提供類必要的構造器.另一個類中使用這些公共方法,計算三角形的面積
*/
public class TriAngleTest {
public static void main(String[] args) {
TriAngle t1 = new TriAngle();
t1.setBase(3.2); // 相當于傳入實參
t1.setHeight(4.66);
System.out.println("base: " + t1.getBase() + ", height: " + t1.getHeight());
// 構造器一方面可創(chuàng)建對象,同時可給相應對象的屬性賦值
TriAngle t2 = new TriAngle(5.1, 4.6);
System.out.println("base: " + t2.getBase() + ", height: " + t2.getHeight());
}
}
public class TriAngle {
private double base;
private double height;
public TriAngle(){
}
public TriAngle(double b,double h){
base = b;
height = h;
}
public void setBase(double b){
base = b;
}
public double getBase(){
return base;
}
public void setHeight(double h){
height = h;
}
public double getHeight(){
return height;
}
}
- 總結: 屬性賦值的先后順序
1.默認初始化(類屬性默認值)
2.顯式初始化(給類中屬性賦值)
3.構造器中初始化(賦值)
=======上面三個操作只能執(zhí)行一次,下面的可以反復執(zhí)行=========
4.未封裝通過"對象.屬性"的方式,賦值或封裝后通過"對象.方法名()",賦值
以上操作的先后順序: 1 - 2 - 3 - 4,從后往前覆蓋,前面的都作為過程出現(xiàn)
public class UserTest {
public static void main(String[] args) {
User u = new User();
System.out.println(u.age);
User u1 = new User(2);
u1.setAge(3);
System.out.println(u1.age);
}
}
class User{
String name;
int age = 1;
public User(){
}
public User(int a){
age = a;
}
public void setAge(int a){
age = a;
}
}
4.9.關鍵字: this的使用---this調(diào)用屬性和方法
this關鍵字的使用:
- 1.this可用來修飾: 屬性,方法,構造器
- 2.this修飾屬性和方法:
this理解為: 當前對象或當前正在創(chuàng)建的對象
- 2.1.在類的方法中,我們課使用"this.屬性"或"this.方法"的方式,調(diào)用當前對象屬性或方法.
但是,通常情況下,我們都選擇省略"this. ".特殊情況下,如果方法的形參和類的屬性同名時,必須顯式的使用"this.變量"的方式,表明此變量是屬性,而非形參.- 2.2.在類的構造器中,我們可使用"this.屬性"或"this.方法"的方式,調(diào)用當前正在創(chuàng)建的對象屬性或方法,但是,通常情況下,都選擇省略"this.",特殊情況下,如果構造器的形參和類的屬性同名時,必須顯式的 使用"this.變量"的方式,表明此變量是屬性,而非形參.
public class PersonTest {
public static void main(String[] args){
Person p1 = new Person(); // 使用默認的空參構造器
p1.setAge(1);
System.out.println(p1.getAge());
p1.eat();
}
}
class Person{
private String name; // 類的成員變量(屬性)
private int age;
public Person(){
this.eat(); // 調(diào)當前正在創(chuàng)建的對象的eat方法
}
// 構造器中的this: 當前正在創(chuàng)建的對象
public Person(String name){
this.name = name; // 當前正在創(chuàng)建的對象的屬性
}
public Person(int age){
this.age = age;
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name){ // 屬性和形參(局部變量)同名不報錯因為地位不同,一個在類中一個在方法中
this.name = name; // 就近原則,優(yōu)先考慮近的變量名
}
public String getName(){
return this.name;
}
public void setAge(int age){ // age = 1
// age = age; // 1 = 1,跟屬性age無關了
// this: 可理解為當前對象,相當于p1
this.age = age; // 當前對象的屬性/方法 通過對象調(diào)方法或?qū)傩远加命c
}
public int getAge(){
return this.age;
}
public void eat(){
System.out.println("人吃飯");
this.study();
}
public void study(){
System.out.println("人學習");
}
}
-
this調(diào)用構造器
this關鍵字的使用:
1.this可用來修飾: 屬性,方法,構造器
2.this修飾屬性和方法:
this理解為: 當前對象或當前正在創(chuàng)建的對象
2.1.在類的方法中,我們課使用"this.屬性"或"this.方法"的方式,調(diào)用當前對象屬性或方法.
但是,通常情況下,我們都選擇省略"this. ".特殊情況下,如果方法的形參和類的屬性同名時,
必須顯式的使用"this.變量"的方式,表明此變量是屬性,而非形參.
2.2.在類的構造器中,我們可 使用"this.屬性"或"this.方法"的方式,調(diào)用當前正在創(chuàng)建的對象屬性或方法,
但是,通常情況下,都選擇省略"this.",特殊情況下,如果構造器的形參和類的屬性同名時,必須顯式的
使用"this.變量"的方式,表明此變量是屬性,而非形參.
3.this調(diào)用構造器
(1)在類的構造器中,可顯式的使用"this(形參列表)"方式,調(diào)用本類中指定的其他構造器
(2)構造器中不能通過"this(形參列表)"方式調(diào)用自己
(3)如果一個類中由n個構造器,則最多有n-1個構造器中使用了"this(形參列表)"
(4)規(guī)定: "this(形參列表)"必須聲明當前構造器的首行
(5)構造器內(nèi)部,最多只能聲明一個"this(形參列表)",用來調(diào)用其他的構造器
public class PersonTest {
public static void main(String[] args){
Person p1 = new Person(); // 使用默認的空參構造器
p1.setAge(1);
System.out.println(p1.getAge());
p1.eat();
System.out.println();
// 構造器多重調(diào)用也只是創(chuàng)建了一個對象,只是調(diào)用其他構造器時邏輯借用了其他構造器的邏輯
// 不管通過什么方式調(diào)用構造器,最終也只是造了一個對象
Person p2 = new Person("Harry", 20);
System.out.println(p2.getAge());
System.out.println(p2.getName());
}
}
class Person{
private String name; // 類的成員變量(屬性)
private int age;
public Person(){
// this.eat(); // 調(diào)當前正在創(chuàng)建的對象的eat方法
// this(); // 空參構造器不能調(diào)自己,會死循環(huán),兩個構造器也不能來回調(diào)
String info = "Person初始化時,需要考慮如下的1,2,3,4...(共40行代碼)";
System.out.println(info);
}
// 構造器中的this: 當前正在創(chuàng)建的對象
public Person(String name){
this(); // 調(diào)用構造器的方法,參數(shù)是什么,就是調(diào)哪個構造器
this.name = name; // 當前正在創(chuàng)建的對象的屬性
}
public Person(int age){
this();
this.age = age;
}
public Person(String name, int age){
this(age);
this.name = name; // 再初始化獨立的name
// this.age = age; 調(diào)用上面的構造器后可省略
}
public void setName(String name){ // 屬性和形參(局部變量)同名不報錯因為地位不同,一個在類中一個在方法中
this.name = name; // 就近原則,優(yōu)先考慮近的變量名
}
public String getName(){
return this.name;
}
public void setAge(int age){ // age = 1
// age = age; // 1 = 1,跟屬性age無關了
// this: 可理解為當前對象,相當于p1
this.age = age; // 當前對象的屬性/方法 通過對象調(diào)方法或?qū)傩远加命c
}
public int getAge(){
return this.age;
}
public void eat(){
System.out.println("人吃飯");
this.study();
}
public void study(){
System.out.println("人學習");
}
}
- 綜合練習
public class CustomerTest {
public static void main(String[] args) {
Customer cust = new Customer("Jane", "Smith");
Account acct = new Account(1000, 2000, 0.0123); // 聲明構造器
cust.setAccount(acct); // 銀行辦卡后得到了卡號
cust.getAccount().deposit(100); // 類變量的運用
cust.getAccount().withdraw(960);
cust.getAccount().withdraw(2000);
System.out.println("Customer [" + cust.getLastName() + ","
+ cust.getFirstName() + "] has a account: id is "
+ cust.getAccount().getId() + ", annualInterrestRate is"
+ cust.getAccount().getAnnualInterestRate() * 100
+ "%, balance is " + cust.getAccount().getBalance());
}
}
public class Account {
private int id; // 賬號
private double balance; // 余額
private double annualInterestRate; // 年利率
// 構造器
public Account(int id,double balance,double annualInterestRate){
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public void setId(int id){
this.id = id;
}
public int getId(){
return this.id;
}
public void setBalance(double balance){
this.balance = balance;
}
public double getBalance(){
return this.balance;
}
public void setAnnualInterestRate(double annualInterestRate){
this.annualInterestRate = annualInterestRate;
}
public double getAnnualInterestRate(){
return this.annualInterestRate;
}
public void withdraw(double amount){ // 取錢
if (balance < amount){
System.out.println("余額不足,取錢失敗");
return; // 和else作用相同
}
balance -= amount;
System.out.println("成功取出" + amount);
}
public void deposit(double amount){ // 存錢
if (amount > 0){
balance += amount;
System.out.println("成功存入" + amount);
}
}
}
public class Customer {
private String firstName;
private String lastName;
private Account account;
// 構造器
public Customer(String f,String l){
this.firstName = f;
this.lastName = l;
}
public String getFirstName(){
return this.firstName;
}
public String getLastName(){
return this.lastName;
}
public void setAccount(Account account){
this.account = account;
}
public Account getAccount(){
return this.account;
}
}
- 綜合練習2: 對象數(shù)組
public class BankTest {
public static void main(String[] args) {
Bank bank = new Bank();
bank.addCustomer("Jane","Smith");
// 連續(xù)操作 可能會空指針,方法要有返回值才能連續(xù)操作
bank.getCustomer(0).setAccount(new Account(2000));
bank.getCustomer(0).getAccount().withdraw(500);
double balance = bank.getCustomer(0).getAccount().getBalance();
System.out.println("客戶:" + bank.getCustomer(0).getLastName() + "的賬戶余額為: " + balance);
bank.addCustomer("萬里","楊");
System.out.println("銀行客戶個數(shù): " + bank.getNumberOfCustomers());
}
}
public class Customer {
private String firstName;
private String lastName;
private Account account;
// 構造器
public Customer(String f,String l){
this.firstName = f;
this.lastName = l;
}
// get,set方法
public String getFirstName(){
return this.firstName;
}
public String getLastName(){
return this.lastName;
}
public void setAccount(Account acct){
this.account = acct;
}
public Account getAccount(){
return this.account;
}
}
public class Account {
private double balance;
public Account(double init_balance){
this.balance = init_balance;
}
// 余額查詢
public double getBalance(){
return this.balance;
}
// 存錢操作
public void deposit(double amt){
if (amt > 0){
balance = balance + amt;
System.out.println("存款成功");
}
}
// 取錢操作
public void withdraw(double amt){
if (balance > amt){
balance = balance - amt;
System.out.println("取錢成功");
}else{
System.out.println("余額不足");
}
}
}
public class Bank {
// 定義了變量,未初始化,但是屬性是已經(jīng)默認初始化了的,此時默認值為null
private Customer[] customers; // 存放多個用戶的數(shù)組
private int numberOfCustomers; // 記錄客戶的個數(shù)
// 構造器
public Bank(){
customers = new Customer[10]; // 先要給數(shù)組初始化空間 因為權限是private所以不能再測試類main里初始化
}
// 添加客戶方法
public void addCustomer(String f,String l){
// cust是customer類型變量,上面定義的數(shù)組也是customer類型的,所以可以賦值
Customer cust = new Customer(f, l);
// customers[numberOfCustomers] = cust; // 可以理解為 customers[numberOfCustomers] = new Customer(f,l); 數(shù)組元素在構造器中靜態(tài)賦值
// numberOfCustomers++;
// customers = new Customer[10]; // 數(shù)組空間初始化不能寫在這,因為,本來是是造構造器中造好一個數(shù)組后掉一個方法存一個人再調(diào)方法再存一個人,寫在方法里變成,先調(diào)方法造好一個數(shù)組后再存一個人,再調(diào)一次方法又重新造一個數(shù)組再重新存一個人
customers[numberOfCustomers++] = cust;
}
// 獲取客戶個數(shù)的方法
public int getNumberOfCustomers(){
return numberOfCustomers;
}
// 獲取指定位置上的客戶的方法
public Customer getCustomer(int index){
// return customers[index]; // 不能這樣寫,可能報異常1.數(shù)組未初始化時可能空指針,2.數(shù)組初始化后可能會數(shù)組下標越界,3.index也許不會超出數(shù)組長度,但是大于實際存入的數(shù)組元素個數(shù)也不行.
if (index >= 0 && index < numberOfCustomers){ // 索引值小于客戶數(shù),因為第一個客戶放在了第0位
return customers[index];
}
return null;
}
}
4.10.1.package關鍵字的使用
- 1.為了更好地實現(xiàn)項目中類的管理,提供包的概念
- 2.使用package聲明類或接口所屬的包,聲明在源文件的首行
- 3.包,屬于標識符,遵循標識符的命名規(guī)則,規(guī)范(xxx.yyy.zzz),"見名知意"
- 4.每"."一次,就代表一層文件目錄.
補充:同一個包下不能命名同名的接口,類
不同的包下,可以命名同名的接口,類
4.10.2.import關鍵字的使用
5.1.面向?qū)ο筇卣髦? 繼承性的理解
1.為什么要有類的繼承性?(繼承性的好處)
1.減少了代碼的冗余,提高了代碼的復用性
2.便于功能的擴展: 把子類都想擴展的功能直接聲明在父類當中,子類又因為繼承了,直接就可以拿到
3.為之后多態(tài)性的使用,提供了前提
2.繼承性的格式:
class A extends B{}
A: 子類,派生類,subclass
B: 父類,超類,基類,superclass
3.子類繼承父類以后有哪些不同?
2.1.體現(xiàn): 一旦子類A繼承父類B以后,子類A中就獲取了父類B中聲明的所有:屬性,方法 (用來調(diào)用)
特別地,父類中聲明為private的屬性或方法,子類繼承父類以后,仍然認為獲取了父類中私有的結構
只有因為封裝性的影響,使得子類不能直接調(diào)用父類的結構而已
2.2.子類繼承父類以后,還可以聲明自己特有的屬性或方法,實現(xiàn)自己特有的功能的拓展
子類和父類的關系,不同于子集和集合的關系(父類有的子類一定有,子類有的父類不一定有)
4.Java中繼承的說明
1.一個父類可以被多個子類繼承.
2.Java中類的單繼承性:一個類只能有一個父類;(接口可以多繼承)
3.子父類是相對的概念
4.子類直接繼承的父類,稱為: 直接父類;簡介繼承的父類稱為:間接父類
5.子類繼承父類后,就獲取了直接父類以及所有間接父類中聲明的屬性和方法
public class ExtendsTest {
public static void main(String[] args) {
Person p1 = new Person();
// p1.age = 1;
p1.eat();
Student s1 = new Student();
// s1.age = 1;
s1.eat(); // 私有化的方法也被繼承,
// s1.sleep();
s1.setAge(10);
System.out.println(s1.getAge());
s1.breath();
Creature c = new Creature();
System.out.println(c.toString());
}
}
public class Creature {
public void breath(){
System.out.println("呼吸");
}
}
public class Person extends Creature {
private int age;
String name;
// 定義構造器
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
// 定義方法
public void eat(){
System.out.println("吃飯");
sleep();
}
private void sleep(){
System.out.println("睡覺");
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return this.age;
}
}
// Student繼承Person類的屬性,功能...
public class Student extends Person{
// Person中定義過的可以省略
/*String name;
int age;*/
String major; // 專業(yè) 對繼承的不滿意可以自己創(chuàng)建新的
// 定義構造器 和類重名不能刪
public Student(){
}
public Student(String name,int age, String major){
this.name = name;
setAge(age);
this.major = major;
}
// 定義方法
/*public void eat(){
System.out.println("吃飯");
}
public void sleep(){
System.out.println("睡覺");
}*/
// 獨有的方法也不能刪
public void study(){
System.out.println("學習");
}
public void show(){
System.out.println("name: " + name + ", age = " + getAge());
}
}
- 繼承性練習:
public class CylinderTest {
public static void main(String[] args) {
Cylinder cy = new Cylinder();
cy.setRadius(1.2);
cy.setLength(3.4);
double volume = cy.findVolume();
System.out.println("圓柱的體積為: " + volume);
// 沒有重寫findArea方法時
/*double area = cy.findArea();
System.out.println("底面圓的面積: " + area);*/
// 重寫findArea方法后
double area = cy.findArea();
System.out.println("圓柱的表面積: " + area);
System.out.println("========================");
// 體現(xiàn)子類默認調(diào)用父類空參構造器:super()
Cylinder cy1 = new Cylinder(); // 輸出: 3.1415926...
double volume1 = cy1.findVolume();
System.out.println("圓柱的體積為: " + volume1);
}
}
public class Circle {
private double radius; // 半徑
public Circle(){
radius = 1.0;
}
public Circle(double radius){
this.radius = radius;
}
public void setRadius(double radius){
this.radius = radius;
}
public double getRadius(){
return this.radius;
}
// 計算面積
public double findArea(){
return Math.PI * radius * radius;
}
}
/*
* 如果把父類空參構造器注釋掉子類空參構造器報錯是因為子類構造器先執(zhí)行父類的空參構造器super();而父類沒有空參構造器
* 如果再把子類的空參構造器注釋掉,子類類名又會報錯,因為,子類有系統(tǒng)默認提供的空參構造器,構造器還是要調(diào)用父類的空參構造器super();而父類沒有空參構造器
*/
public class Cylinder extends Circle{
private double length;
public Cylinder(){
// 實際默認有個super(); // 通過子類構造器造對象,他一上來就奔到父類空參構造器初始化半徑
length = 1.0;
}
public void setLength(double length){
this.length = length;
}
public double getLength(){
return this.length;
}
// 返回圓柱體積
public double findVolume(){
// return Math.PI * getRadius() * getRadius() * length;
// return findArea() * getLength(); // 子類把父類方法重寫覆蓋了,現(xiàn)在調(diào)用的是子類的findArea方法,所以不能用這個方法算
return super.findArea() * getLength(); // 調(diào)用父類的findArea方法
}
// 返回圓柱表面積
public double findArea(){
return Math.PI * getRadius() * getRadius() * 2 + Math.PI * getRadius() * 2 * getLength();
}
}
5.java.lang.Object類的理解
特別聲明是java.lang包下的Object類因為可能自己會創(chuàng)建同名的Object類
1.如果我們沒有先是的聲明一個類中的父類的話,則此類繼承于java.lang.Object類
2.所有的java類(除java.lang.Object類之外)都直接或間接的繼承于java.lang.Object類
3.所有java類具有java.lang.Object類聲明的功能
5.2.方法重寫的理解(override/overwrite)
方法重寫(override/ overwrite): 方法名形參相同,只改變方法體.權限范圍可相同或更大(除private外),返回值類型相同或更大
1.重寫: 子類繼承父類后,可以對父類中同名同參數(shù)的方法,進行覆蓋操作,執(zhí)行的就是自己的
2.應用: 重寫以后,當創(chuàng)建子類對象以后,通過子類對象調(diào)用子父類中的同名同參數(shù)的方法時,實際執(zhí)行的是子類重寫父類的方法(相當于子類自己定義的)
3.應用: 重寫規(guī)定:
方法的聲明: 權限修飾符 返回值類型 方法名(形參列表){
方法體
}
約定俗稱: 子類中的叫重寫的方法,父類中的叫被重寫的方法
(1)子類重寫的方法的方法名和形參列表與父類被重寫的方法的方法名和形參列表相同
(2)子類重寫的方法的權限修飾符不小于父類被重寫的方法的權限修飾符
特殊情況: 子類不能重寫父類中聲明為private權限的方法
(3)返回值類型:
父類被重寫的方法的返回值類型是void,則子類重寫的方法的返回值類型只能是void
父類被重寫的方法的返回值類型是A類型,則子類重寫的方法的返回值類型可以是A類或A類的子類
父類被重寫的方法的返回值類型是基本數(shù)據(jù)類型(如:double),則子類重寫的方法的返回值類型是必須相同的基本數(shù)據(jù)類型(必須也是double)(自動類型提升不等同于子父類關系)
(4)子類重寫的方法拋出的異常類型不大于父類被重寫的方法拋出的異常類型,(具體到異常處理時候講)
========================================================================
子類和父類中的同名同參數(shù)的方法要么都聲明為非static的(才考慮重寫),要么都聲明為static的(不是重寫),因為靜態(tài)方法不能被覆蓋,是隨著類的加載而加載的
面試題: 區(qū)分方法的重載和重寫
public class PersonTest {
public static void main(String[] args) {
Student s = new Student("計算機科學與技術");
s.eat();
s.walk(10); // 顯然這里walk方法里調(diào)的是父類的show方法,如果是重寫,子類的show方法會把父類的覆蓋
System.out.println("============");
s.study();
Person p1 = new Person();
p1.eat(); // 父類的對象調(diào)用父類的方法,顯示的還是父類的,自己定義就調(diào)自己
}
}
public class Person {
String name;
int age;
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃飯");
}
public void walk(int distance){
System.out.println("走路的距離是 " + distance + " 公里");
show();
eat();
}
// 父類中的聲明為private權限的方法不能被子類重寫
private void show(){
System.out.println("我是人");
}
// 父類被重寫的方法的返回值類型要大于等于子類重寫的方法
public Object info(){
return null;
}
public double info1(){
return 1.0;
}
}
public class Student extends Person{
String major;
public Student(){
}
public Student(String major){
this.major = major;
}
public void study(){
System.out.println("學習專業(yè)是: " + major);
}
// 對父類中eat()進行了重寫
public void eat(){
System.out.println("學生應都吃有營養(yǎng)的事物");
}
public void show(){
System.out.println("我是學生");
}
public String info(){
return null;
}
// 子類重寫方法的返回的基本數(shù)據(jù)類型必須和父類被重寫的相同
/*public int info1(){
return 1;
}*/
}
5.3.四種權限訪問修飾符
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
order.orderDefault = 1;
order.orderProtected = 2;
order.orderPublic = 3;
order.methodDefault();
order.methodProtected();
order.methodPublic();
// 同一個包中的其他類,不可以調(diào)用Order類中私有的屬性,方法
/*order.orderPrivate = 4;
order.methodPrivate();*/
}
}
public class Order {
private int orderPrivate;
int orderDefault;
protected int orderProtected;
public int orderPublic;
private void methodPrivate(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
void methodDefault(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
protected void methodProtected(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
public void methodPublic(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
}
==============================================
import com.oop.demo02.PermissionModifier.Order;
public class OrderTest {
public static void main(String[] args) {
// 通過造對象體現(xiàn)
Order order = new Order();
order.orderPublic = 1;
order.methodPublic();
// 不同包下的普通類(非子類)要使用Order類,不可以調(diào)用聲明為private,缺省,protected權限的屬性,方法
/*order.orderPrivate = 2;
order.orderDefault = 3;
order.orderProtected = 1;
order.methodPrivate();
order.methodDefault();
order.methodProtected();*/
}
// 通過不造對象體現(xiàn),但是調(diào)用(形參)實際還是要造對象
public void show(Order order){
order.orderPublic = 1;
order.methodPublic();
// 不同包下的普通類(非子類)要使用Order類,不可以調(diào)用聲明為private,缺省,protected權限的屬性,方法
/*order.orderPrivate = 2;
order.orderDefault = 3;
order.orderProtected = 1;
order.methodPrivate();
order.methodDefault();
order.methodProtected();*/
}
}
import com.oop.demo02.PermissionModifier.Order;
public class SubOrder extends Order {
public void method(){
// 因為已經(jīng)繼承了父類,相當于調(diào)用自己的屬性和方法,所以不用new對象也能調(diào)用
// 繼承后直接可以賦值,相當于繼承幫你聲明了
orderProtected = 1;
orderPublic = 2;
methodProtected();
methodPublic();
// 在不同包的子類中,不能調(diào)用父類Order類中聲明private和缺省權限的屬性,方法
/*orderDefault = 3;
orderPrivate = 2;
methodDefault();
methodPrivate();*/
}
}
5.4.super關鍵字的使用
1.super理解為:父類的
2.super可以用來調(diào)用(修飾):屬性,方法,構造器,調(diào)的父類(就近原則),this也一樣
3.super的使用
3.1.可以在子類類的方法或構造器中.通過使用"super.屬性"或"super.方法"的方式,顯式的調(diào)用父類中聲明的屬性或方法.但是,通常情況下,習慣省略"super.",(在父類有,子類沒有屬性或方法的情況下)
3.2.特殊情況下,當子類和父類中定義了同名的屬性時,要想在子類中調(diào)用父類中聲明的屬性,則必須顯式的使用"super.屬性"的方式,表明調(diào)用的是父類中聲明的屬性.
3.3.特殊情況,當子類重寫了父類中的方法后,想在子類的方法中調(diào)用父類中被重寫的方法時,則必須顯式的使用"super.方法"的方式,表明調(diào)用的是父類中被重寫的方法.
4.super調(diào)用構造器
4.1.可以在子類的構造器中顯式的使用"super(形參列表)"的方式,調(diào)用父類中聲明的指定的構造器
4.2."super(形參列表)"的使用,必須聲明在子類構造器的首行!
4.3.在類的構造器中,針對"this(形參列表)"(表示調(diào)用本類中重載的其他構造器或"super(形參列表)"(表示父類中指定的構造器),只能二選一,不能同時出現(xiàn)
4.4.在構造器的首行,沒有顯式的聲明"this(形參列表)"或:super(形參列表)",則默認調(diào)用的是父類中空參構造器:super()
4.5.一個類的構造器可能有多個,構造器的首行不是this(形參列表),就是super(形參列表),只能二選一,不可能有其他情況
一個類可能有n個構造器,至少有一個用super方式調(diào)用了父類的構造器super();最多有n-1個使用了this(形參列表),剩下一個沒用的只能是用了super();
public class SuperTest {
public static void main(String[] args) {
Student s = new Student();
s.show();
s.study();
Student s1 = new Student("tom", 21, "IT");
s1.show();
System.out.println("=================");
Student s2 = new Student();
}
}
public class Creature {
int age = 1;
}
public class Person extends Creature {
String name;
int age;
int id = 1001; // 身份證號
public Person(){
System.out.println("我無處不在!");
}
public Person(String name){
this.name = name;
}
public Person(String name,int age){
this(name);
this.age = age;
}
public void eat(){
System.out.println("人: 吃飯");
}
public void walk(){
System.out.println("人: 睡覺");
}
}
public class Student extends Person{
String major;
int id = 1002; // 學號 屬性此時在內(nèi)存中有兩個id,屬性不像方法一樣會覆蓋掉父類的
public Student(){
super(); // 編譯器先執(zhí)行了父類的構造方法才執(zhí)行子類的操作
}
public Student(String major){
super(); // 子類中每個構造器都默認隱式的調(diào)用了父類的空參構造器,
this.major = major;
}
// super使用構造器
public Student(String name, int age, String major){
// (有繼承權無使用權)繼承是子類繼承父類的屬性和方法,但調(diào)用了private修飾的屬性和方法,子類不能直接訪問
super(name,age); // 調(diào)用父類指定參數(shù)的構造器
this.major = major;
}
@Override
public void eat() {
System.out.println("學生多吃有營養(yǎng)的東西");
}
public void study(){
System.out.println("學生學習知識");
eat(); // 默認都是this.eat(),調(diào)用重寫以后的方法,
super.eat(); // 調(diào)用父類的方法
walk(); // 調(diào)用子類中未重寫父類中的方法,方法前寫this,super都可以通常省略掉了,區(qū)別在this先在子類中找,找沒才去父類中找,super直接去父類(包括直接父類和間接父類)中找,直到找到為止
}
public void show(){
// this: 先在當前的Student類當中找看看有無該屬性的結構,如果在自己類里沒找到,他再接著去父類中找
// super: 不在(當前)子類找,直接去父類找
System.out.println("name = " + this.name + ", age = " + super.age);
System.out.println("id = " + id); // id前省略的是: this.
System.out.println("id = " + super.id);
}
}
5.5.子類對象實例化過程
子類對象實例化的全過程
如果沒有明確地指出父類,Object類就是該類的父類,是默認的,而且不用顯式地寫出來
1.從結果上來看:(繼承性)
子類繼承父類后,就獲取了父類聲明的屬性或方法
創(chuàng)建子類的對象,在堆空間中,就會加載所有父類(直接父類,間接父類)中聲明的屬性
2.從過程上來看:
當通過子類的構造器創(chuàng)建子類對象時,一定會直接或間接的調(diào)用其父類的構造器,進而調(diào)用父類的父類的構造器...
直到調(diào)用了java.lang.Object類中空參的構造器為止.正因為家在過所有的父類的結構,所以才可以看到內(nèi)存中有父類中的結構,子類對象才可以考慮進行調(diào)用
明確: 雖然創(chuàng)建子類對象時,調(diào)用了父類的構造器,但是自始至終就創(chuàng)建過一個對象,即為new的子類對象,因為堆中值分配了一個地址
- 練習: 繼承&super
public class AccountTest {
public static void main(String[] args) {
Account acct = new Account(1122, 20000, 0.045);
acct.withdraw(30000);
System.out.println("賬戶余額: " + acct.getBalance());
acct.withdraw(2500);
System.out.println("賬戶余額: " + acct.getBalance());
acct.deposit(3000);
System.out.println("賬戶余額: " + acct.getBalance());
System.out.println("月利率: " + (acct.getMonthInterest() * 100) + "%");
}
}
public class Account {
private int id; // 賬號
protected double balance; // 余額
private double annualInterestRate; // 年利率
// 子類構造器/類名報錯解決辦法: 1.給父類造空參構造器 2.給子類造指定構造器
public Account(int id, double balance, double annualInterestRate){
super();
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public void setId(int id){
this.id = id;
}
public int getId(){
return this.id;
}
public void setBalance(double balance){
this.balance = balance;
}
public double getBalance(){
return this.balance;
}
public void setAnnualInterestRate(double annualInterestRate){
this.annualInterestRate = annualInterestRate;
}
public double getAnnualInterestRate(){
return this.annualInterestRate;
}
// 返回月利率
public double getMonthInterest(){
return this.annualInterestRate / 12;
}
// 取錢
public void withdraw(double amount){
if (balance >= amount){
balance -= amount;
return;
}
System.out.println("余額不足");
}
// 存錢
public void deposit(double amount){
if (amount > 0){
balance += amount;
}
}
}
public class CheckAccount extends Account{
private double overdraft; // 可透支限額
public CheckAccount(int id, double balance, double annualInterestRate, double overdraft){
super(id,balance,annualInterestRate);
this.overdraft = overdraft;
}
public void setOverdraft(double overdraft){
this.overdraft = overdraft;
}
public double getOverdraft(){
return this.overdraft;
}
@Override
public void withdraw(double amount) {
// 余額足夠消費
if (this.getBalance() >= amount){
// 方式一:
// setBalance(getBalance() - amount);
// 方式二:
super.withdraw(amount); // super.屬性/方法可以調(diào)用父類的屬性和方法
// 方式三: 改balance的權限為protected
// balance = balance - amount;
// 透支+余額足夠消費
}else if (overdraft >= amount - getBalance()){
overdraft = overdraft - (amount - getBalance()); // 先從可透支余額取出
// setBalance(0); // 再把余額全清空
// 或
super.withdraw(getBalance());
}else{
System.out.println("超過可透支限額");
}
}
}
public class CheckAccountTest {
public static void main(String[] args) {
CheckAccount acct = new CheckAccount(1122, 20000, 0.045, 5000);
acct.withdraw(5000);
System.out.println("賬戶余額: " + acct.getBalance() + ", 可透支余額: " + acct.getOverdraft());
acct.withdraw(18000);
System.out.println("賬戶余額: " + acct.getBalance() + ", 可透支余額: " + acct.getOverdraft());
acct.withdraw(3000);
System.out.println("賬戶余額: " + acct.getBalance() + ", 可透支余額: " + acct.getOverdraft());
}
}
5.6.多態(tài)性的使用
面向?qū)ο筇卣髦? 多態(tài)性
1.理解多態(tài)性: 可以理解為一個事物的多種形態(tài)
2.何為多態(tài)性: 父類的引用指向子類的對象(或子類的對象賦給父類的引用)
3.多態(tài)的使用: 虛擬方法使用
有了對象的多態(tài)性后,在編譯期,只能調(diào)用父類中聲明的方法,但在運行期,實際執(zhí)行的是子類重寫父類的方法
如果子類中沒有重寫父類的方法,就執(zhí)行父類的方法(這樣使用多態(tài)性沒有意義)
總結:編譯看等號左邊的類型,運行看右邊的對象(編譯時是父類,執(zhí)行時是子類)
4.多態(tài)性的使用前提(只說方法的事沒有屬性的事,多態(tài)性方面跟屬性沒關系): (1)要有類的繼承關系 (2)方法的重寫
5.對象的多態(tài)性: 只適用于重寫的方法,不適用于屬性(屬性的編譯和運行都看左邊).
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man man = new Man();
man.eat();
man.age = 25;
man.earnMoney();
System.out.println("*********************");
// 對象的多態(tài)性: 父類的引用(p2)指向子類的對象(new Man())
Person p2 = new Man(); // 多態(tài)的形式
Person p3 = new Woman();
// 多態(tài)的使用: 當調(diào)用子類同名同參數(shù)的方法時,實際執(zhí)行的是子類重寫父類的方法---虛擬方法調(diào)用
// 有了多態(tài)的形式后,通過引用調(diào)用子父類都聲明過的方法,真正執(zhí)行的是子類重寫父類的方法
p2.eat(); // 編譯看左,運行看右
p2.walk();
// p2.earnMoney(); // 多態(tài)性的使用不能調(diào)父類沒有的方法
System.out.println(p2.id);
}
}
public class Person {
String name;
int age;
int id = 1001;
public void eat(){
System.out.println("人吃飯");
}
public void walk(){
System.out.println("人走路");
}
}
public class Man extends Person{
boolean isSmoking;
int id = 1002;
public void earnMoney(){
System.out.println("男人負責掙錢");
}
/*@Override
public void eat() {
System.out.println("男人多吃肉,長肌肉");
}*/
@Override
public void walk() {
System.out.println("男人霸氣的走路");
}
}
public class Woman extends Person{
boolean isBeauty;
public void goShopping(){
System.out.println("女人喜歡購物");
}
@Override
public void eat() {
System.out.println("女人少吃為了減肥");
}
@Override
public void walk() {
System.out.println("女人窈窕的走路");
}
}
- 多態(tài)性的使用舉例:
import java.sql.Connection;
// 多態(tài)性的使用舉例一:
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.func(new Dog());
test.func(new Cat());
}
// 使用多態(tài)性: 凡是new子類的對象
// 每種動物對一件事表現(xiàn)出來的形態(tài)都不一樣 就是多態(tài)
public void func(Animal animal){ // Animal animal = new Dog(); 對象多態(tài)性的形式
// 因為形參是Animal類型的,所以只能調(diào)Animal類里的方法,但實際new了一個Dog類的對象,真正運行的時候,是Dog重寫父類方法的執(zhí)行
animal.eat();
animal.shout();
}
// 不用多態(tài)性的寫法: 聲明什么類型只能new這個類型的對象
public void func(Dog dog){
dog.eat();
dog.shout();
}
public void func(Cat cat){
cat.eat();
cat.shout();
}
}
// 父類
class Animal{
public void eat(){
System.out.println("動物進食");
}
public void shout(){
System.out.println("動物叫");
}
}
// 子類
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨頭");
}
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("貓吃魚");
}
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
// 舉例二:
class Order{
public void method(Object obj){
}
}
// 舉例三:
class Driver{
// 形參里傳哪個對象就建立跟哪個數(shù)據(jù)庫的連接,因為子類方法都重寫過了,自然就能實現(xiàn)對那個數(shù)據(jù)庫中表的操作
public void doData(Connection conn){ // conn = new MySQLConnection();/ conn = new OracleConnection();/...
// 規(guī)范步驟去操作數(shù)據(jù) 都是子類重寫過的方法
/*conn.method1();
conn.method2();
conn.method3();*/
}
}