Day07-Java
1、static關鍵字
1.1 使用static關鍵字定義屬性
在講解static定義屬性操作之前,首先撰寫如下一道程式,
現在定義一個表示中國人的操作類,所有人所在的國家為中國,
class Person{
private String name;
private int age;
String country = "中國";
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getInfo(){
return "姓名:"+this.name+" "+"年齡:"+this.age+" "+"國家:"+country;
}
}
public class TestDemo1{
public static void main(String args[]){
Person per1 = new Person("張三",30);
Person per2 = new Person("李四",40);
Person per3 = new Person("王五",50);
System.out.println(per1.getInfo());
System.out.println(per2.getInfo());
System.out.println(per2.getInfo());
}
}

很明顯,現在不管有多少個Person物件他的屬性country的內容都是相同的,如果這時中國改名了,那么所有Person類的country屬性都好更改,這樣的話太過繁瑣,
現在可以發現這樣的問題:
1、country屬性應該是一個公共的內容,但上面的代碼是將其分配給每一個物件;
2、對country屬性維護的時候要考慮的物件太多了,不便于維護,
所以如果想將country與普通屬性相區分,并表示公共的概念,則應該使用static關鍵字完成,
class Person{
private String name;
private int age;
static String country = "中國";
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getInfo(){
return "姓名:"+this.name+" "+"年齡:"+this.age+" "+"國家:"+country;
}
}
public class TestDemo1{
public static void main(String args[]){
Person per1 = new Person("張三",30);
Person per2 = new Person("李四",40);
Person per3 = new Person("王五",50);
per1.country = "美國";
System.out.println(per1.getInfo());
System.out.println(per2.getInfo());
System.out.println(per2.getInfo());
}
}

那么現在對一個物件的屬性country進行修改就一定會影響到其他物件的country屬性,但是這里有個問題既然使用了static定義的屬性表示一個公共屬性,那么如果現在又一個物件進行修改是不合適的,應該有所有物件的集合的最大代表,就是類,即:static屬性最好的呼叫,是通過“類名稱.static屬性”的方式來完成,
Person.country = “美國”; //類名稱.static屬性
通過本程式,就因該清楚一下幾點:
1 使用static定義的屬性不在堆記憶體之中保存,保存在全域資料區;
2 使用static定義的屬性表示類屬性,類屬性可以由型別名稱直接進行呼叫;
3static屬性雖然定義在類之中,但是其可以在沒有實體化物件的時候進行呼叫(普通屬性保存在堆記憶體之中,static屬性保存在全域資料區)
在以后的開發之中,首先想到的不是static屬性,而是普通屬性,
1.2 使用static定義方法
class Person{
private String name;
private int age;
//static String country = "中國";
private static String country = "中國";
public Person(String name,int age){
this.name = name;
this.age = age;
}
//無法從靜態背景關系中參考非靜態 變數 this
//靜態方法中沒有辦法使用this變數
public static void setCountry(String c){
country = c;
}
public static String getCountry(){
return country;
}
public String getInfo(){
return "姓名:"+this.name+" "+"年齡:"+this.age+" "+"城市:"+country;
}
}
public class TestDemo1{
public static void main(String args[]){
/*Person per1 = new Person("張三",30);
Person per2 = new Person("李四",40);
Person per3 = new Person("王五",50);
per1.country = "美國";
System.out.println(per1.getInfo());
System.out.println(per2.getInfo());
System.out.println(per2.getInfo());*/
//呼叫的方法必須為靜態方法
//姓名:張三 年齡:30 城市:中國人民共和國
Person.setCountry("中國人民共和國");//沒有實體化物件產生
Person p1 = new Person("張三",30);
System.out.println(p1.getInfo());
}
}
以上的代碼如果單獨觀察都不難理解,主要都是由于static定義的特殊性所決定的,但是現在的類之中就比較熱鬧了,分成了兩類:static和非static,而對于方法的操作有如下兩定義:
1、static定義的方法不能呼叫非static的方法或是屬性;
2、非static定義的方法卻可以呼叫static方法和static屬性;
討論:為什么要有這樣的限制?
使用static定義的屬性和方法,可以在沒有實體化物件的時候呼叫,而非static方法必須在實體化物件之后才能呼叫,
1.3 分析主方法
在講解主方法之前,思考一下小小的區別:在之前講解Java方法定義格式的時候給過以下格式:如果一個方法在主類之中定義,并且有主方法直接呼叫的時候,前面必須有public static,
public class TestDemo{
public static void main(String args[]){
print(); //主方法直接呼叫
}
public static void print(){
System.out.println("Hello World");
}
}
按照之前所學習的概念來講,此時,表示的是一個static方法呼叫其他的static方法,但是如果這個時候的print方法上沒有static呢?所有非static方法幾乎都有一個特點:非方法要由實體化物件呼叫,
public class TestDemo{
public static void main(String args[]){
//Hello,World!
new TestDemo().print(); //實體化物件呼叫非static方法
}
public void print(){
System.out.println("Hello,World!");
}
}
主方法的組成:
-
Public:表示一個訪問權限,表示公共
-
static:此方法由類名稱直接呼叫,執行類:Java 類名稱
-
void:主方法是一切的開始
-
main:系統規定的一個方法名稱,執行類的時候默認找到此名稱
-
String args[]:表示的是一些運行時引數,通過字串接收
public class TestDemo2{
public static void main(String args[]){
for(int x = 0;x<args.length;x++){
System.out.println(args[x]);
}
}
}

1.4 關鍵字的使用
在實際的作業之中,使用static的原因有兩個:
- 希望可以在沒有實體化物件的時候可以輕松的執行類的某些操作
- 現在希望表示出資料共享的概念
統計一個類的實體化物件的個數
class Person{
private String name;
static int num = 0;
public Person(){
num++;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public class TestDemo3{
public static void main(String args[]){
Person per1 =new Person();
Person per2 =new Person();
Person per3 =new Person();
Person per4 =new Person();
System.out.println(Person.num);
}
}
2、代碼塊
2.1 普通代碼塊
public class PuTongDaiMaKuai{
public static void main(String args[]){
{ //普通代碼塊
int x = 10; //區域變數
System.out.println("x ="+x);
}
int x = 100; //全域變數
System.out.println("x ="+x);
}
}
//x =10
//x =100
2.2 構造塊
class Person{
public Person(){
System.out.println("構造方法1")
}
{
System.out.println("構造塊2");
}
}
public class PuTongDaiMaKuai{
public static void main(String args[]){
new Person();
new Person();
new Person();
}
}
/*
構造塊2
構造方法1
構造塊2
構造方法1
構造塊2
構造方法1
*/
可以發現,構造塊優先于構造方法先執行,而且每當有一個新的實體化物件產生的時候,就會出現構造塊的執行,
2.3 靜態塊
靜態塊也是定義在類之中的,如果一個構造塊上使用了static關鍵字進行定義的話,那么就表示靜態塊,但是靜態塊要考慮兩種情況,
情況一:在非主類之中定義的靜態塊
class Person{
public Person(){
System.out.println("構造方法1");
}
{//構造塊
System.out.println("構造塊2");
}
static{//靜態塊
System.out.println("靜態塊3");
}
}
public class JingTaiKuai{
public static void main(String args[]){
new Person();
new Person();
new Person();
}
}/*構造塊3構造塊2構造方法1構造塊2構造方法1構造塊2構造方法1*/
可以發現,靜態塊優先于構造塊,而且不管產生多少實體化物件,靜態塊只呼叫一次,
情況二:在主類中定義的靜態塊
public class TestDemo4{
static{
System.out.println("靜態塊");
}
public static void main(String args[]){
System.out.println("主方法");
}
}
/*
靜態塊
主方法
*/
在主類之中的靜態塊優先于主方法執行,那么既然靜態塊有這個特點,我們可以加以利用,
3、繼承性
Person java:
class Person{
private String name;
private int age;
public void setName(String name){
this.name=name;
}
public void setAge(int age){
this.age=age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
}
Student java:
class Student{
private String name;
private int age;
private String school;
public void setName(String name){
this.name=name;
}
public void setAge(String age){
this.age=age;
}
public void setSchool(String school){
this.school=school;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public String getSchool(){
return school;
}
}
可以發現里面有一大部分的內容已經重復了,但是現在拋開程式不談,學生和人的關系是:學生就是一個人,但是學生類比人類的定義更加嚴格,此時按照之前的概念肯定無法解決問題,
3.1 繼承的實作
在Java中,如果想要明確的實作繼承的關系,則可以使用extends關鍵字完成,格式如下:
//但是在這里需要注意的是:子類又被稱為派生類;
//父類也被稱為超類(super class)
class 子類 extends 父類{}
class Person{
private String name;
private int age;
public void setName(String name){
this.name=name;
}
public void setAge(int age){
this.age=age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
}
class Student extends Person
{
}
public class TestDemo5{
public static void main(String args[]){
Student stu = new Student();
stu.setName("張三");
System.out.println(stu.getName());
}
}
通程序式的運行我可以發現,現在在子類之中,沒有定義任何方法,而是可以直接將父類中定義的操作繼續使用,所以在子類之中可以對父類進行重用,當然,子類也可以定義屬于自己的操作,
class Person{
private String name;
private int age;
public void setName(String name){
this.name=name;
}
public void setAge(int age){
this.age=age;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
}
class Student extends Person{
private String school;
public void setSchool(String school){
this.school = school;
}
public String getSchool(){
return this.school;
}
}
public class TestDemo5{
public static void main(String args[]){
Student stu = new Student();
stu.setName("張三");
stu.setSchool("賓夕法尼亞大學");
//name = 張三,school = 賓夕法尼亞大學
System.out.println("name = " + stu.getName()+ ",school = " +stu.getSchool());
}
}
通過以上程式了一發現,在開發語言之中,子類最基本的功能就是維持父類的原本操作,所以在程式語言之中并不會存在所謂的現實生活中的“敗家子”的概念,所以通程序式可以發現,繼承的功能就是對已有類功能的一種擴充,
3.2 繼承的限制
- 在使用繼承關系后,子類物件在實體化物件前一定首先實體化父類構造方法,再呼叫子類的構造方法后子類再進行實體化操作,——現實生活之中,沒有老子,也絕對沒有后代,肯定是老子先出來,之后在輪到小的再出來,所以這樣的實體化方式非常吻合現實生活,如果在呼叫子類構造之前先去呼叫父類構造就可以證明是先實體化父類物件,而后再實體化子類物件,
- Java中只允許單繼承,不允許多繼承,
- 在進行繼承的時候,子類會繼承父類所有的結構,所有的非私有操作采用顯式繼承的方式,所有的私有操作采用隱式繼承的方式,——按之前的開發所講,一個類中的全部內容肯定都被子類繼承,對于之前學生和人之間的操作,學生類也繼承了Person類中的name和age兩個屬性,這兩個屬性是間接繼承(隱式繼承),只能通過方法訪問
class Person{
public Person(){
System.out.println("父類構造");
}
}
class Student extends Person{
public Student(){
//既然是進行構造方法的呼叫,必須寫再首行位置
super();//此陳述句再無參時寫與不寫一樣
System.out.println("子類構造");
}
}
public class JiChengXing{
public static void main(String args[]){
new Student();
}
}
而如果說現在父類中沒有明確的無參構造方法,這個時候就可以利用super()來指定要呼叫的父類構造引數,
class Person{
public Person(String name,int age){
System.out.println("父類構造");
}
}
class Student extends Person{
public Student(String name,int age,String school){
super(name,age);
System.out.println("子類構造");
}
}
public class JiChengXing{
public static void main(String args[]){
new Student("Jokser",22,"北京大學");
}
}
4、覆寫
既然現在出現類繼承的關系,就出現類子類和父類之間的聯系,而在子類之中有可能定義了和父類完全相同的方法或屬性的名稱,這個時候就成為覆寫,
4.1 方法的覆寫
當子類定義了和父類在方法名稱、回傳值型別、引數型別以及個數完全相同的方法的時候,成為方法的覆寫,
class A{
public void print(){
System.out.println("我是A的輸出方法");
}
}
class B extends A{
public void print(){
System.out.println("我是B的輸出方法");
}
}
public class TestDemo6{
public static void main(String args[]){
B b=new B();
//我是B的輸出方法
b.print();
}
}
當我們一個類之中的方法被覆寫之后,如果實體化的是這個子類物件,則被呼叫的方法就是被覆寫過的方法,
但是在進行方法覆寫的時候有一個點需要注意:被子類所覆寫的方法不能擁有比父類更嚴格的訪問控制權限,對于訪問控制權限現在已經接觸過三種:private<default(不寫)<public,
如果此時父類的訪問權限是default權限,那么子類覆寫的時候只能是default或public權限,而如果父類的方法是public,那么子類之中的覆寫方法訪問權限只能是public,
提示:別沒事找事干!以后只要是方法都是public型別,屬性就寫private型別
當一個子類覆寫了一個父類方法的時候,那么在這種情況下,如果子類想要呼叫父類的被覆寫過的方法,則在方法前加上“super”,
class A{
public void print(){
System.out.println("我是A的輸出方法");
}
}
class B extends A{
public void print(){
//如果想在子類中呼叫父類的方法就在子類首行加super.方法名
super.print();
System.out.println("我是B的輸出方法");
}
}
public class TestDemo6{
public static void main(String args[]){
B b=new B();
//我是B的輸出方法
b.print();
}
}
🌝面試題:多載和覆寫的區別?在進行多載的時候回傳值是否可以不同?
| 區別 | 多載 | 覆寫 |
|---|---|---|
| 英文單詞 | OverLoding | Overrider |
| 概念 | 方法名稱相同,引數型別以及個數不同 | 方法名稱、引數型別、引數個數必須全部相同 |
| 范圍 | 發生在一個類里面 | 發生在多個類里面 |
| 限制 | 沒有要求 | 被覆寫的方法不能有比父類更高的訪問控制權限 |
方法多載的時候回傳型別是可以不同的,但是良好的設計要求是型別一致,
4.2 屬性覆寫
當一個子類定義了和父類重名的屬性名稱的時候,就表示屬性的覆寫了,
class A{
String str = "Hello World";
}
class B extends A{
int str = 100;
public void print(){
System.out.println(this.str);
System.out.println(super.str);
}
}
public class FuXie{
public static void main(String args[]){
B b=new B();
//100
//Hello World
b.print();
}
}
這種操作幾乎沒有意義,因為從開發來講,屬性一定進行封裝,封裝之后就沒有覆寫了,
4.3 super關鍵字
🌝面試題:this和super的區別?
| 區別 | this | super |
|---|---|---|
| 定義 | 表示本類物件 | 表示父類物件 |
| 使用 | 本類操作:this()、this.屬性、this.方法 | super()、super.屬性、super.方法 |
| 呼叫構造方法 | 呼叫本類構造方法放在首行 | 子類呼叫父類構造放在首行 |
| 查找范圍 | 先從本類中,查找不到父類 | 直接由子類查找父類 |
| 特殊 | 表示當前物件 | 無 |
5、陣列操作(定義Array父類)
**要求:**現在要求定義一個整型陣列的操作類,陣列的大小由外部決定,用戶可以向陣列之中增加資料,以及取得陣列中的全部資料,也可以根據外部提供的陣列的增長大小,在原本的陣列之上擴充指定的容量,另外,在此類上派生兩個子類:
? 排序類(sortArray):取得陣列內容是經過排序出來的結果;
? 反轉類(Fanzhuan):取得陣列的內容是反轉出來的結果;
首先要完成的是定義父類,根本就不需要考慮子類,
class Array{
private int data[];//定義一個陣列,陣列大小由外部決定
private int foot = 0;//陣列角標
//創建陣列
public Array(int len){
if(len > 0){
this.data = new int [len];//開辟空間
}else{
this.data = new int [1];
}
}
//增加陣列
public boolean add(int num){
if(this.foot >= data.length){
return false;
}else{
this.data[this.foot ++] = num;
return true;
}
}
//輸出陣列
public int[] getData(){
return this.data;
}
//動態擴充陣列
public void inc(int num){
int [] newData = new int [this.data.length + num];
/*
public static void arraycopy(Object src, src - 源陣列,
int srcPos, srcPos - 源陣列中的起始位置,
Object dest, dest - 目標陣列,
int destPos, destPos - 目的地資料中的起始位置,
int length) length - 要復制的陣列元素的數量,
*/
System.arraycopy(this.data,0,newData,0,this.data.length);
this.data = newData;
}
}
//對陣列進行排序操作
class sortArray extends Array{
public sortArray(int num){
super(num);
}
public int[] getData(){
java.util.Arrays.sort(super.getData());
return super.getData();
}
}
//陣列內容翻轉
class Fanzhuan extends Array{
public Fanzhuan(int num){
super(num);
}
public int[] getData(){
int head = 0;
int tail = super.getData().length - 1;
int center = super.getData().length / 2;
for(int i = 0 ; i < center ; i++,head++,tail--){
int temp = super.getData()[head];
super.getData()[head] = super.getData()[tail];
super.getData()[tail] = temp;
}
return super.getData();
}
}
public class Exam1{
public static void main(String args[]){
//Array arr = new Array(5);
//sortArray arr = new sortArray(5);
Fanzhuan arr = new Fanzhuan(5);
System.out.println(arr.add(3));
System.out.println(arr.add(1));
System.out.println(arr.add(3));
System.out.println(arr.add(8));
System.out.println(arr.add(0));
arr.inc(3);
System.out.println(arr.add(7));
System.out.println(arr.add(20));
System.out.println(arr.add(10));
int result[] = arr.getData();
for(int i = 0; i < result.length ; i++){
System.out.print(result[i]+"、");
}
}
}

在整個程式開發之中,可以明顯的感覺到,所有的操作都是圍繞著父類功能的擴充進行的,但是方法并沒有改變,所以在開發之中,父類的設計是最重要的,子類最好繼承或者說是覆寫覆寫操作,都是應該以父類的方法為主,
6、final關鍵字
在Java中,final關鍵字表示的是一個終結器的概念,使用final可以定義類、方法、變數,
使用final定義的類不能有子類
final class A{
}
class B extends A{
}
使用final定義的方法不能被子類所覆寫
final class A{
public final void print(){}
}
class B extends A{
public void print(){}
}
使用final定義的變數,就表示常量,常量在定義的時候必須設定默認值,并且無法修改
final class A{
final String str = "hello world";//常量
public final void print(){
str = "你好"; //無法修改
}
}
public class Final{
public static void main(String args[]){
}
}
而如果說現在使用了public static定義的常量,那么這個常量就成為全域常量,
public static final String STR = "hello,world";//全域常量
而對于以上final關鍵字的三個操作,只有全域常量的概念是被開發之中所使用的,像類或方法定義上使用的情況,幾乎不會再我們編程中出現,
定義final常量的時候每個單詞都要大些,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/291846.html
標籤:java
