Java基礎知識複習2--面向物件篇
所以內容均來自於b站“遇見狂神說”
構造器
一旦定義了一個有參構造,則必須顯示的定義一個無參構造!!
package com.objectOriented.test1;
public class Student {
String name;
int age;
public Student() {//有了有參構造必須定義一個無參的
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
mac中的構造快捷鍵是Command+n
小解物件記憶體分析
推薦原講解視訊:建立物件記憶體分析
三大特徵之“封裝”
三大特徵之“繼承”
mac的idea中檢視繼承關係的快捷鍵是ctrl+h
super
私有的東西無法被”直接繼承“,既不能被直接訪問但是可以間接的訪問,比如上面通過上面的封裝操作:一個方法在父類中是public修飾的,則可以在子類中直接呼叫;倘若是private修飾的,則不可以在子類中使用super關鍵字直接呼叫,應當通過父類中的getset方法既封裝來操作!
呼叫父類的構造方法,必須放在子類構造器的第一行(既首先呼叫父類的構造方法),否則報錯
public Class Person{ public Person(){ System.out.printLn("Person的無參構造執行了!") } }
public Class Student extends Person{
public Student(){
//super(); //這個是隱藏的,預設的可以不寫,倘若要寫就一定要放在第一行!!!!
System.out.printLn("Student的無參構造執行了!")
}
}
強烈建議子類一定要把無參和有參的構造寫一個出來
方法重寫
注意,方法重寫和方法過載是不一樣的概念!!重寫都是方法的重寫,和屬性無關!
重寫關鍵字:Override
測試一:
public class A { public static void test(){//靜態方法 System.out.println("A->test"); } }
public class B extends A{
public static void test(){//靜態方法
System.out.println("B->test");
}
}
public class Application {
public static void main(String[] args) {
B b=new B();
b.test();
A a=new B();//父類的引用指向了子類
a.test();
}
}
執行結果是:
測試二:
public class A{
public void test(){ //沒有static修飾
System.out.println("A->test");
}
}
public class B extends A{
@Override//重寫 必須要有
public void test() {//同上
super.test();
}
}
public class Application {
public static void main(String[] args) {
B b=new B();
b.test();
A a=new B();//父類的引用指向了子類
a.test();
}
}
執行結果:
為什麼上面的差距這麼大,借用彈幕的話:
因為靜態方法是類的方法,而非靜態類是物件的方法,有static時,a呼叫了A類的方法,因為a是用A類定義的;沒有static時,A呼叫的是物件的方法,而a是用B類new的,既a是B類new出來的物件,因此呼叫了B的方法!
至於為什麼可以這樣寫,這是多型的內容,重寫就是為了多型而生!的!
A a=new B();
另外,靜態方法只能被繼承,不能被重寫
說到底,其本質是:a(地址)引用,指向了B類物件,所以呼叫的就是B類的方法
三大特徵之多型(*******)
舉個簡單的例子,上面繼承那一欄裡面:
A a=new B();
既多型,父類引用子類物件!因為子類繼承了父類的全部!!這樣寫是沒有問題的!
測試一:
public class Person {
public void run(){
System.out.println("父類的run");
}
}
public class Student extends Person{}
public class Application {
public static void main(String[] args) {
Student s1=new Student();
Person p1=new Student();
s1.run();
p1.run();
}
}
執行:
很好理解,父類和子類都有run方法,父類呼叫子類物件的方法,由於子類的run方法繼承的是父類的沒有改變,所以還是父類的run方法!
測試二:子類重寫父類方法
public class Person {
public void run(){
System.out.println("父類的run");
}
}
public class Student extends Person{
@Override
public void run() {
System.out.println("子類的run");
}
}
public class Application {
public static void main(String[] args) {
Student s1=new Student();
Person p1=new Student();
s1.run();
p1.run();
}
}
執行結果:
同上,父類呼叫子類的run方法,但是由於子類重寫了run方法,所以執行的是子類重寫後的run方法!
測試三:父類呼叫子類新增的方法
public class Person {
public void run(){
System.out.println("父類的run");
}
}
public class Student extends Person{
@Override
public void run() {
System.out.println("子類的run");
}
public void eat(){
System.out.println("子類的eat");
}
}
public class Application {
public static void main(String[] args) {
Student s1=new Student();
Person p1=new Student();
s1.run();
p1.eat();//一定會報錯
}
}
這個很好理解,因為父類沒有該方法!能否執行看引用型別(左),執行內容看實際型別(右)
所以,假如是Person p1=new Student()這種,在與static無關的情況下,父類要執行的方法必須要是父子兩個都有的!假如子類沒有重寫,則父類呼叫的還是父類本身的方法(子類繼承的是父類的沒變),子類重寫了則呼叫的是子類重寫的方法!
//Student能呼叫的方法都是自己的或者繼承父類的
Student s1=new Student();
//父型別,可以指向之類,但是不能呼叫子類獨有的方法
Person p1=new Student();
instanceof關鍵字
判斷兩個類之間是否存在父子關係
public class Person {
public void run(){
System.out.println("父類的run");
}
}
public class Student extends Person{
@Override
public void run() {
System.out.println("子類的run");
}
}
public class Teacher extends Person{
@Override
public void run() {
super.run();
}
}
public class Application {
public static void main(String[] args) {
//Object->Person->Student
//Object->Person->Teacher
//Object->String
Object object=new Student();
System.out.println(object instanceof Student);
System.out.println(object instanceof Person);
System.out.println(object instanceof Object);
System.out.println(object instanceof Teacher);
System.out.println(object instanceof String);
}
}
執行:
同理:
Person person=new Student();
System.out.println(person instanceof Student);
System.out.println(person instanceof Person);
System.out.println(person instanceof Object);
System.out.println(person instanceof Teacher);
//System.out.println(person instanceof String);
執行:
首先要明白一個規矩,既java遵循”編譯看左(引用型別),執行看右(實際指向型別)“的規矩,所以最後一行程式碼報錯,既首先編譯就通不過,因為Person類和String是平級,沒有父子關係!第4個是false是因為編譯的時候看左(Person類)和Teacher有父子關係,可行!實際執行的時候看右(Student類)和 Teacher屬於同級沒有關係!
型別之間的轉換:父與子
同基本資料型別一樣,低轉高自動轉換,但是高轉低就需要強制了!
public class Person {
public void run(){
System.out.println("父類的run");
}
}
public class Student extends Person{
@Override
public void run() {
System.out.println("子類的run");
}
public void go(){
System.out.println("子類的獨有的go方法");
}
}
子轉父:低轉高,向上轉型,直接轉,丟失子類中原本可以直接呼叫的特有方法
Student s1=new Student();
s1.go();
Person p1=s1;
//p1.go();程式碼報錯,因為沒有go方法(既會丟失子類特有的方法)
父轉子:高轉低,向下轉型,強制轉,丟失父類被子類所重寫掉的方法!
Person p2=new Student();
//p2不能使用Student獨有的方法,如若使用強制轉換
Student s1=(Student)p2;//高轉低(右轉左)需要強制轉換
s1.go();
//或者
((Student)p2).go();
static關鍵字總結
用在方法上叫靜態方法(也可以叫類方法,通過類名訪問),用在屬性上叫靜態屬性(如靜態變數,也叫類變數)!
加了static關鍵字的,是從屬於這個類的,別人用不了,只有本類能用!
public class Test1 {
{
//匿名程式碼塊,賦初始值
System.out.println("匿名程式碼塊!");
}
static {
//靜態程式碼塊,跟類一載入就執行,永久只執行一次!
System.out.println("靜態程式碼塊!");
}
public Test1(){
//構造器
System.out.println("建構函式!");
}
public static void main(String[] args) {
Test1 t1=new Test1();
System.out.println("=============");
Test1 t2=new Test1();
}
}
執行:
同樣的,一個類被final修飾了就不能被繼承了,稱之為斷子絕孫修飾符
抽象類
介面(*********)
//interface 定義的關鍵字 介面都需要有實現類
public interface UserService {
//介面中的屬性預設都是靜態常量,既public static final
int AGE=10;
//介面中的所有定義預設都是抽象的public,既定義的時候public abstract可以不用寫
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
public interface TimeService {
void timer();
}
//類只能單繼承,而介面可以多繼承,且必須重新接口裡面的方法
public class UserServiceImpl implements UserService,TimeService{
@Override
public void add(String name) {}
@Override
public void timer() {}
@Override
public void delete(String name) {}
@Override
public void update(String name) {}
@Override
public void query(String name) {}
}
總結:
內部類(面試愛問)
所謂內部類就是在一個類的內部再定義一個類,比如A類中定義一個B類,則B類稱之為A類的內部類,A類稱之為B類的外部類!
成員內部類
public class Outer {
private int id;
public void out(){
System.out.println("這是外部類的方法");
}
public class Inner{
public void in(){
System.out.println("這是內部類的方法");
}
}
}
public class Application {
public static void main(String[] args) {
Outer outer=new Outer();
//內部類的建立格式
Outer.Inner inner=outer.new Inner();
inner.in();
}
}
執行:
那麼內部類能幹嘛呢,我們通過內部類可以獲得外部類的私有屬性和方法
靜態內部類
public class Outer {
private int id;
public void out(){
System.out.println("這是外部類的方法");
}
public static class Inner{
public void in(){
System.out.println("這是內部類的方法");
}
public void getId(){
//System.out.println(id);會報錯
}
}
}
上面會報錯的原因是因為:按照我們之前在static總結的來分析,類的執行(載入)順序是靜態->匿名->建構函式!所以,假如上面的的程式碼要想通過,得把id也用static修飾才可以!
區域性內部類
public class Outer{
//區域性內部類
public void method(){
class Inner2{
public void in(){}
}
}
}
需要說明的是,一個java檔案裡面只能有一個public class,但是卻可以有多個class,既:
public class A{}
//public class A{} 錯誤
class C{}
class D{}
......
匿名內部類
public class test {
public static void main(String[] args) {
new Apple().eat();
}
}
class Apple{
public void eat(){
System.out.println("吃了蘋果");
}
}
執行:
你甚至還可以在下面寫上一個介面,當然這個就不做描述了!
異常
三種異常:
捕獲和丟擲異常
public class Exception1 {
public static void main(String[] args) {
int a=10;
int b= 0;
System.out.println(a/b);
}
}
如何捕獲這異常呢?-----------使用try/catch語句
public class Exception1 {
public static void main(String[] args) {
int a=10;
int b= 0;
try {//try表示監控區域(有問題轉到catch)
System.out.println(a/b);
}catch (ArithmeticException e){//catch為捕獲異常
System.out.println("算術執行異常!");
}finally {//處理善後工作
System.out.println("over");
}
//finally可以不要,但是像涉及到IO,資源的關閉可以放到finally裡!
}
}
另外,try/catch也可以像if/else一樣可以有多個catch:
public class Exception2 {
public static void main(String[] args) {
try {
new Exception2().a();
}catch (Error e){
System.out.println("Error");
}catch (Exception e){
System.out.println("Exception");
}catch (Throwable e){
System.out.println("Throwable");
}finally {
System.out.println("over");
}
}
public void a(){
b();
}
public void b(){
a();
}
}
注意,異常等級越高的一定要放在後面!根據先後執行的順序,滿足了就不會執行後面的語句了!
不知道是什麼異常可以這樣寫:
try{
.....
}catch(Exception e){
e.printStackTrace();//打印出具體的異常資訊!
}
異常的丟擲
public class Exception3 {
public static void main(String[] args) {
try{
new Exception3().test(3,0);
}catch (ArithmeticException e) {
e.printStackTrace();
}
}
//假設這個方法中處理不了這個異常,則可以主動丟擲異常,讓更高級別的去處理
public void test(int a,int b){
if(b==0){
throw new ArithmeticException();
} else{
System.out.println(a/b);
}
}
}
自定義異常
//自定義一個異常類,要繼承Exception
public class MyException extends Exception{
private int detail;
public MyException(int a){
this.detail=a;
}
//toString:異常的列印資訊
@Override
public String toString() {
return "MyException{"+detail+"}";
}
}
public class Test {
//可能會存在異常的方法
static void test(int a) throws MyException {//注意,這裡是throws不是throw
System.out.println("傳遞的引數為:"+a);
if(a>10){
throw new MyException(a);//處理為要麼這裡try/catch,要麼丟擲去
}
System.out.println("OK");
}
public static void main(String[] args) {
try {
test(11);
}catch (MyException e){
//e.printStackTrace();
System.out.println("MyException>>"+e);//e即為執行toString()
}
}
}