一、類及物件
1. 類的組成成分
- 屬性(成員變數,Field)
- 方法(成員方法,函式,Method)
2. 屬性
成員變數 vs 區域變數
- 相同點:
- 遵循變數宣告的格式: 資料型別 變數名 = 初始化值
- 都有作用域
- 不同點:
- 宣告的位置的不同 :成員變數:宣告在類里,方法外, 區域變數:宣告在方法內,方法的形參部分,代碼塊內
- 成員變數的修飾符有四個:public private protected 預設,區域變數沒有修飾符,與所在的方法修飾符相同
- 初始化值:一定會有初始化值,成員變數:如果在宣告的時候,不顯式的賦值,那么不同資料型別會有不同的默認初始化值, 區域變數:一定要顯式的賦值,(區域變數沒有默認初始化值)
- byte short int long ==>0
- float double ==>0.0
- char ==>空格
- boolean ==>false
- 參考型別變數==>null
- 二者在記憶體中存放的位置不同:成員變數存在于堆空間中;區域變數:堆疊空間中
- 總結:
- 按照資料型別的不同:基本資料型別(8種) & 參考資料型別
- 按照宣告的位置的不同:成員變數 & 區域變數
3. 方法
提供某種功能的實作
public void eat(){//方法體}
public String getName(){}
public void setName(String n){}
//格式:權限修飾符 回傳值型別(void:無回傳值/具體的回傳值) 方法名(形參){}
- 關于回傳值型別
- void:表明此方法不需要回傳值
- 有回傳值的方法:在方法的最后一定有return + 回傳值型別對應的變數
- 方法內可以呼叫本類的其他方法或屬性,但是不能在方法內再定義方法!
4. 面向物件編程的思想的落地法則一:
- 設計并創建類及類的成分
- 實體化類的物件
- 通過“物件.屬性”或"物件.方法"的形式完成某項功能
5. 類的初始化記憶體決議:記憶體劃分的結構
- 堆疊(stack):區域變數 、物件的參考名、陣列的參考名
- 堆(heap):new 出來的“東西”(如:物件的物體,陣列的物體),含成員變數
- 方法區:含字串常量
- 靜態域:宣告為static的變數
6. 萬事萬物皆物件
- 在Java語言范疇中,我們都能將功能、結構等封裝到類中,通過類的物體化,來呼叫具體的功能結構
- Scanner、String
- 檔案:File
- 設計到Java語言與前端Html、后端的資料庫互動時,都體現為類、物件
7. 例題
/*
* 4. 物件陣列題目:
定義類Student,包含三個屬性:學號number(int),年級state(int),成績score(int),
創建20個學生物件,學號為1到20,年級和成績都由亂數確定,
問題一:列印出3年級(state值為3)的學生資訊,
問題二:使用冒泡排序按學生成績排序,并遍歷所有學生資訊
提示:
1) 生成亂數:Math.random(),回傳值型別double;
2) 四舍五入取整:Math.round(double d),回傳值型別long,
*
*
* // 兩位數的,亂數 10 - 99
公式:【a,b】 : Math.random()*(b-a+1)+a 再強轉資料型別,
*
*/
public class StudentTest {
public static void main(String[] args) {
// 宣告Student型別的陣列
Student[] stu = new Student[20];
for (int i = 0; i < stu.length; i++) {
// 給陣列元素賦值
stu[i] = new Student();
stu[i].number = i + 1;
// [1,6]
stu[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
// [0,100]
stu[i].score = (int) (Math.random() * (100 - 0 + 1));
}
StudentTest test = new StudentTest();
// 問題1
test.searchState(stu,3);
System.out.println("------------------------");
//
// 問題2
test.sort(stu);
test.print(stu);
}
// 遍歷學生陣列
public void print(Student[] stu) {
for (int i = 0; i < stu.length; i++) {
System.out.println(stu[i].info());
}
}
/**
*
* @Description 查找指定年紀的學生
* @author MD
* @date 2020年7月6日下午12:02:56
* @param stu 查找的陣列
* @param state 指定的年紀
*/
public void searchState(Student[] stu, int state) {
for (int i = 0; i < stu.length; i++) {
if (stu[i].state == 3)
System.out.println(stu[i].info());
}
}
public void sort(Student[] stu) {
for (int i = 0; i < stu.length - 1; i++) {
for (int j = 0; j < stu.length - i - 1; j++) {
if (stu[j].score <= stu[j + 1].score) {
// 注意,這里交換的不是成績而是物件
Student temp = stu[j];
stu[j] = stu[j + 1];
stu[j + 1] = temp;
}
}
}
}
}
class Student {
int number;
int state;
int score;
public String info() {
return "學號:" + number + " 年級:" + state + " 分數:" + score;
}
}
二、方法的多載(overload)
要求:
* 同一個類中
* 方法名必須相同
* 方法的引數串列不同(①引數的個數不同②引數型別不同)
補充:方法的多載與方法的回傳值型別沒有關系!
//如下的四個方法構成多載
//定義兩個int型變數的和
public int getSum(int i,int j){
return i + j;
}
//定義三個int型變數的和
public int getSum(int i,int j,int k){
return i + j + k;
}
//定義兩個double型資料的和
public double getSum(double d1,double d2){
return d1 + d2;
}
//定義三個double型陣列的和
public void getSum(double d1,double d2,double d3){
System.out.println(d1 + d2 + d3);
}
//不能與如上的幾個方法構成多載
// public int getSum1(int i,int j,int k){
// return i + j + k;
// }
// public void getSum(int i,int j,int k){
// System.out.println(i + j + k);
// }
//以下的兩個方法構成多載,
public void method1(int i,String str){
}
public void method1(String str1,int j){
}
三、可變個數的形參的方法
- .格式:對于方法的形參: 資料型別 ... 形參名
- 可變個數的形參的方法與同名的方法之間構成多載
- 可變個數的形參在呼叫時,個數從0開始,到無窮多個都可以
- 使用可變多個形參的方法與方法的形參使用陣列是一致
- 若方法中存在可變個數的形參,那么一定要宣告在方法形參的最后
- 在一個方法中,最多宣告一個可變個數的形參
//如下四個方法構成多載
//在類中一旦定義了多載的可變個數的形參的方法以后,如下的兩個方法可以省略
// public void sayHello(){
// System.out.println("hello world!");
// }
// public void sayHello(String str1){
// System.out.println("hello " + str1);
// }
//可變個數的形參的方法
public void sayHello(String ... args){
for(int i = 0;i < args.length;i++){
System.out.println(args[i] + "$");
}
//System.out.println("=====");
}
public void sayHello(int i,String ... args){
//public void sayHello(String ... args,int i){
System.out.println(i);
for(int j = 0;j < args.length;j++){
System.out.println(args[j] + "$");
}
}
public void sayHello1(String[] args){
for(int i = 0;i < args.length;i++){
System.out.println(args[i]);
}
}
四、 Java的值傳遞
- 方法的引數傳遞(重點、難點)
- 形參:方法宣告時,方法小括號內的引數
- 實參:呼叫方法時,實際傳入的引數的值
- java中的引數傳遞機制:值傳遞機制
- 形參是基本資料型別的:將實參的值傳遞給形參的基本資料型別的變數
- 形參是參考資料型別的:將實參的參考型別變數的值(對應的堆空間的物件物體的首地址值)傳遞給形參的參考型別變數
- 關于變數的賦值
- 如果變數是基本資料型別,此時賦值的是變數所保存的資料值
- 如果變數是參考資料型別,此時賦值的變數是所保存的地址值
1. 例一
public static void main(String[] args) {
TestArgsTransfer tt = new TestArgsTransfer();
int i = 10;
int j = 5;
System.out.println("i:" + i + " j:" + j);//i : 10 j : 5
// //交換變數i與j的值
// int temp = i;
// i = j;
// j = temp;
tt.swap(i, j);//將i的值傳遞給m,j的值傳遞給n
System.out.println("i:" + i + " j:" + j);//i : 10 j : 5
}
//定義一個方法,交換兩個變數的值
public void swap(int m,int n){
int temp = m;
m = n;
n = temp;
System.out.println("m:" + m + " n:" + n);
}
2. 例二
public class TestArgsTransfer1 {
public static void main(String[] args) {
TestArgsTransfer1 tt = new TestArgsTransfer1();
DataSwap ds = new DataSwap();
System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j);
tt.swap(ds);
System.out.println(ds);
System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j);
}
//交換元素的值
public void swap(DataSwap d){
int temp = d.i;
d.i = d.j;
d.j = temp;
System.out.println(d);//列印參考變數d的值
}
}
class DataSwap{
int i = 10;
int j = 5;
}
3. 例3

package com.atguigu.exer;
import java.io.PrintStream;
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 10;
method(a,b);
System.out.println("a="+a);
System.out.println("b="+b);
}
// public static void method(int a , int b) {
// a = a * 10;
// b = b * 20;
// System.out.println(a);
// System.out.println(b);
// System.exit(0);
// }
public static void method(int a, int b) {
PrintStream ps = new PrintStream(System.out) {
public void println(String x){
if("a=10".equals(x)) {
x = "a=100";
}else if("b=10".equals(x)) {
x = "b=200";
}
super.println(x);
}
};
System.setOut(ps);
}
}

4. 例4
輸出的什么?
public class Test1 {
public static void main(String[] args) {
int[] arr = new int[] {1,2,3};
System.out.println(arr); //地址值
char[] arr1 = new char[] {'a','b','c'};
System.out.println(arr1); //abc
}
}
五、面向物件的特征一:封裝
- 問題:當創建了類的物件以后,如果直接通過"物件.屬性"的方式對相應的物件屬性賦值的話,可能會出現不滿足實際情況的意外,我們考慮不讓物件來直接作用屬性,而是通過"物件.方法"的形式,來控制物件對屬性的訪問,實際情況中,對屬性的要求就可以通過方法來體現
- 高內聚,低耦合
- 面向物件思想的落地法則二:
- 將類的屬性私有化
- 提供公共的方法(setter & getter)來實作呼叫
- 四種權限修飾符
- 權限從大到小為:public protected 預設 private
- 四種權限都可以用來修飾屬性、方法、構造器
- 修飾類的話:public 預設

- 封裝性的體現
- 將類的屬性私有化,提供公共的方法來呼叫
- 不對外暴露的私有化方法
- 單例模式
1. 構造器
構造器的作用:①創建物件 ②給創建的物件的屬性賦值
- 設計類時,若不顯式宣告類的構造器的話,程式會默認提供一個空參的構造器
- 一旦顯式的定義類的構造器,那么默認的構造器就不再提供
- 如何宣告類的構造器,格式:權限修飾符 類名(形參){ }
- 類的多個構造器之間構成多載
- 類物件的屬性賦值的先后順序:
- 屬性的默認初始化
- 屬性的顯式初始化
- 通過構造器給屬性初始化
- 通過"物件.方法"的方式給屬性賦值
- 一個類中至少會有一個構造器,一般都提供一個空參的構造器
2. this關鍵字
- 使用在類中,可以用來修飾屬性、方法、構造器
- 表示當前物件或者是當前正在創建的物件
- 當形參與成員變數重名時,如果在方法內部需要使用成員變數,必須添加this來表明該變數時類成員
- 在任意方法內,如果使用當前類的成員變數或成員方法可以在其前面添加this,增強程式的閱讀性
- 在構造器中使用“this(形參串列)”顯式的呼叫本類中多載的其它的構造器,要求“this(形參串列)”要宣告在構造器的首行!
public class TestPerson {
public static void main(String[] args) {
Person p1 = new Person();
System.out.println(p1.getName() + ":" + p1.getAge());
Person p2 = new Person("BB",23);
int temp = p2.compare(p1);
System.out.println(temp);
}
}
class Person{
private String name;
private int age;
public Person(){
this.name = "AA";
this.age = 1;
}
public Person(String name){
this(); // 先呼叫空引數的
this.name = name;
}
public Person(String name,int age){
this(name);
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(){
System.out.println("eating");
}
public void sleep(){
System.out.println("sleeping");
this.eat();
}
//比較當前物件與形參的物件的age誰大,
public int compare(Person p){
if(this.age > p.age)
return 1;
else if(this.age < p.age)
return -1;
else
return 0;
}
}
3. package/import
package: 宣告源檔案所在的包,寫在程式的第一行
import:
- 顯式匯入指定包下的類或介面
- 寫在包的宣告和源檔案之間
- 如果需要引入多個類或介面,那么就并列寫出
- 如果匯入的類是java.lang包下的,如:System String Math等,就不需要顯式的宣告
- 理解.*的概念,比如java.util. *;
- 匯入java.lang.*只能匯入lang包下的所有類或介面,不能匯入lang的子包下的類或介面
- import static 表示匯入指定類的static的屬性或方法
/import java.util.Scanner;
//import java.util.Date;
//import java.util.List;
//import java.util.ArrayList;
import java.lang.reflect.Field;
import java.util.*;
import static java.lang.System.*;
public class TestPackageImport {
public static void main(String[] args) {
out.println("helloworld");
Scanner s = new Scanner(System.in);
s.next();
Date d = new Date();
List list = new ArrayList();
java.sql.Date d1 = new java.sql.Date(522535114234L);
Field f = null;
}
}
六、面向物件的特征二:繼承
- 繼承的格式
- 通過"class A extends B"類實作類的繼承
- 子類繼承父類以后,父類中宣告的屬性、方法,子類就可以獲取到
- 當父類中有私有的屬性或方法時,子類同樣可以獲取得到,只是由于封裝性的設計,使得子類不可以直接呼叫罷了
- 子類除了通過繼承,獲取父類的結構之外,還可以定義自己的特有的成分
- java中類的繼承性只支持單繼承:一個類只能繼承一個父類,反之,一個父類可以有多個子類
- 如果沒有顯示宣告一個類的父類的話,則此類繼承于java.lang.Object類
1. 方法的重寫(override orverwrite) vs 多載(overload)
- 多載:“兩同一不同”:同一個類,同一個方法名,不同的引數串列注:方法的多載與方法的回傳值無關!構造器是可以多載的
- 重寫:(前提:在繼承的基礎之上,子類在獲取了父類的結構以后,可以對父類中同名的方法進行“重構”)方法的回傳值,方法名,形參串列形同;權限修飾符不小于父類的同名方法;子類方法的例外型別不大于父類的;兩個方法要同為static或同為非static
- 注:不能重寫父類的私有的方法
- 父類被重寫的回傳值型別是A型別,那么子類重寫方法的回傳值型別可以是A類或者A類的子類
class Cirlce{
//求圓的面積
public double findArea(){
}
}
class Cylinder extends Circle{
//求圓柱的表面積
public double findArea(){
}
}
2. 關鍵字 super
- super,相較于關鍵字this,可以修飾屬性、方法、構造器
- super修飾屬性、方法:在子類的方法、構造器中,通過super.屬性或者super.方法的形式,顯式的呼叫父類的指定屬性或方法,尤其是,當子類與父類有同名的屬性、或方法時,呼叫父類中的結構的話,一定要用“super.”
- 通過“super(形參串列)”,顯式的在子類的構造器中,呼叫父類指定的構造器!
- 任何一個類(除Object類)的構造器的首行,要么顯式的呼叫本類中多載的其它的構造器“this(形參串列)”或顯式的呼叫父類中指定的構造器“super(形參串列)”,要么默認的呼叫父類空參的構造器"super()",只能二選一
- 建議在設計類時,提供一個空參的構造器
3. 子類物件實體化的全程序
無論通過那個構造器創建子類物件,需要保證先初始化父類
目的:當子類繼承父類后,繼承父類中所有的屬性和方法,因此子類必須知道父類如何為物件進行初始化
public class TestDog {
public static void main(String[] args) {
Dog d = new Dog();
d.setAge(10);
d.setName("小明");
d.setHostName("花花");
System.out.println("name:" + d.getName() + " age:" + d.getAge()
+ "hostName:" + d.getHostName());
System.out.println(d.toString());
}
}
// 生物
class Creator {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Creator() {
super();
System.out.println("this is Creator's constructor");
}
}
// 動物類
class Animal extends Creator {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Animal() {
super();
System.out.println("this is Animal's constructor");
}
}
// 狗
class Dog extends Animal {
private String hostName;
public String getHostName() {
return hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
}
public Dog() {
super();
System.out.println("this is Dog's constructor");
}
}
七、 面向物件的特征三:多型
1. 多型性的表現:
①方法的多載與重寫 ②子類物件的多型性
2. 使用的前提:
①要有繼承關系 ②要有方法的重寫
3. 格式
- Person p = new Man();//向上轉型
- 通過父類的參考指向子類的物件物體,當呼叫方法時,實際執行的是子類重寫父類的方法
編譯時,認為p是Person型別的,故只能執行Person里才有的結構,即Man里特有的結構不能夠呼叫,子類物件的多型性,并不使用于屬性,
呼叫方法:編譯看左邊,運行看右邊
屬性:編譯和運行都看左邊
package com.atguigu.java;
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
// 多型性的體現
test.func(new Dag());
test.func(new Cat());
}
public void func(Animal an) { // Animal an = new Dag();
an.eat();
an.shot();
}
// public void func(Dag dag) {
// dag.eat();
// dag.shot();
// }
}
class Animal{
public void eat() {
System.out.println("動物:吃食物");
}
public void shot() {
System.out.println("動物:叫");
}
}
class Dag extends Animal{
public void eat() {
System.out.println("狗吃肉");
}
public void shot() {
System.out.println("汪!汪");
}
}
class Cat extends Animal{
public void eat() {
System.out.println("貓吃魚");
}
public void shot() {
System.out.println("喵!喵");
}
}
4. 關于向下轉型
有了物件的多型性之后,記憶體中實際上是加載了子類特有的屬性和方法,但是由于變數宣告為父型別別,導致了編譯時只能呼叫父類中宣告的屬性和方法,子類中特有的屬性和方法呼叫不了,所以有了向下轉型
- 向下轉型,使用強轉符:()
- 為了保證不報ClassCastException,最好在向下轉型前,進行判斷: instanceof
if (p1 instanceof Woman) {
System.out.println("hello!");
Woman w1 = (Woman) p1;
w1.shopping();
}
if (p1 instanceof Man) {
Man m1 = (Man) p1;
m1.entertainment();
}
5. 多型是編譯性行為還是運行時行為
運行時行為
package com.atguigu.java5;
import java.util.Random;
//面試題:多型是編譯時行為還是運行時行為?
//證明如下:
class Animal {
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat extends Animal {
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("Sheep eat grass");
}
}
public class InterviewTest {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat ();
case 1:
return new Dog ();
default:
return new Sheep ();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animal animal = getInstance(key);
animal.eat();
}
}
6. 問題1
package com.atguigu.exer;
/*
* 練習:
* 1.若子類重寫了父類方法,就意味著子類里定義的方法徹底覆寫了父類里的同名方法,
* 系統將不可能把父類里的方法轉移到子類中:編譯看左邊,運行看右邊
*
* 2.對于實體變數則不存在這樣的現象,即使子類里定義了與父類完全相同的實體變數,
* 這個實體變數依然不可能覆寫父類中定義的實體變數:編譯運行都看左邊
*/
class Base {
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count);
}
}
public class FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
Base b = s;//多型性
//==:對于參考資料型別來講,比較的是兩個參考資料型別變數的地址值是否相同
System.out.println(b == s);//true
System.out.println(b.count);//10
b.display();//20
}
}
7. 問題2
package com.atguigu.exer;
//考查多型的筆試題目:
public class InterviewTest1 {
public static void main(String[] args) {
Base1 base = new Sub1();
base.add(1, 2, 3); //sub_1
Sub1 s = (Sub1)base;
s.add(1,2,3); //sub_2
}
}
class Base1 {
public void add(int a, int... arr) {
System.out.println("base1");
}
}
class Sub1 extends Base1 {
public void add(int a, int[] arr) {
System.out.println("sub_1");
}
public void add(int a, int b, int c) {
System.out.println("sub_2");
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/148113.html
標籤:Java
