02_Java面向对象

第六章 面向对象的基本特征

面向对象的基本特征:

1、封装

2、继承

3、多态

6.1 封装

1、好处:

(1)隐藏实现细节,方便使用者使用

(2)安全,可以控制可见范围

2、如何实现封装?

通过权限修饰符

面试题:请按照可见范围从小到大(从大到小)列出权限修饰符?

修饰符本类本包其他包的子类任意位置
private×××
缺省××
protected×
public

权限修饰符可以修饰什么?

类(类、接口等)、属性、方法、构造器、内部类

类(外部类):public和缺省

属性:4种

方法:4种

构造器:4种

内部类:4种

3、通常属性的封装是什么样的?

当然属性的权限修饰符可以是private、缺省、protected、public。但是我们大多数时候,见到的都是private,然后给它们配上get/set方法。

示例代码:标准Javabean的写法

public class Student{
    //属性私有化
    private String name;
    private int age;
    private boolean marry;
    
    //公共的get/set
    public void setName(String n){
        name = n;//这里因为还没有学习this等,可能还会优化
    }
    public String getName(){
        return name;
    }
    public void setAge(int a){
        age = a;
    }
    public int getAge(){
        return age;
    }
    public void setMarry(boolean m){
        marry = m;
    }
    public boolean isMarry(){//boolean类型的属性的get方法,习惯使用把get换成is
        return marry;
    }
}

6.2 构造器

1、构造器的作用:
(1)和new一起使用创建对象

//调用无参构造创建对象
类名 对象名 = new 类名();

//调用有参构造创建对象
类名 对象名 = new 类名(实参列表);

(2)可以在创建对象的同时为属性赋值

public class Circle{
    private double radius;
    
    public Circle(){
        
    }
    public Circle(double r){
        radius = r;//为radius赋值
    }
}

2、声明构造器的语法格式:

【修饰符】 class 类名{
    【修饰符】 类名(){//无参构造
        
    }
    【修饰符】 类名(形参列表){//有参构造
        
    }
}

3、构造器的特点:

(1)所有的类都有构造器

(2)如果一个类没有显式/明确的声明一个构造器,那么编译器将会自动添加一个默认的无参构造

(3)如果一个类显式/明确的声明了构造器,那么编译器将不再自动添加默认的无参构造,如果需要,那么就需要手动添加

(4)构造器的名称必须与类名相同

(5)构造器没有返回值类型

(6)构造器可以重载

示例代码:

public class Circle{
    private double radius;
    
    public Circle(){
        
    }
    public Circle(double r){
        radius = r;//为radius赋值
    }
}

6.3 关键字this

1、this关键字:

意思:当前对象

(1)如果出现在构造器中:表示正在创建的对象

(2)如果出现在成员方法中:表示正在调用这个方法的对象

2、this的用法:

(1)this.属性

当局部变量与成员变量同名时,那么可以在成员变量的而前面加“this.”用于区别

(2)this.方法

调用当前对象的成员方法,完全可以省略“this.”

(3)this()或this(实参列表)

this()表示调用本类的无参构造

this(实参列表)表示调用本类的有参构造

this()或this(实参列表)要么没有,要么必须出现在构造器的首行

示例代码:

public class Student{
    private String name;
    private int score;
    
    public Student(){
        
    }
    public Student(String name){
        this.name = name;
    }
    public Student(String name, int score){
        this(name);
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setScore(int score){
        this.score = score;
    }
    public int getScore(){
        return score;
    }
}

3、成员变量与局部变量的区别?

这里只讨论实例变量(关于类变量见static部分)

(1)声明的位置不同

成员变量:类中方法外

局部变量:方法中或代码中

​ ①方法的形参列表

​ ②方法体中局部变量

​ ③代码块中的局部变量

(2)运行时在内存中的存储位置不同

成员变量:堆

局部变量:栈

基本数据类型的变量在栈中,引用数据类型的变量在堆中:不准确

(3)修饰符

成员变量:有很多修饰符,例如:权限修饰符

局部变量:不能加权限修饰符,唯一的能加的是final

(4)初始化

成员变量:有默认值

局部变量:没有默认值,必须手动初始化

(5)生命周期

成员变量:随着对象的创建而创建,随着对象被回收而消亡,即与对象同生共死。每一个对象都是独立的。

局部变量:方法调用时才分配,方法运行结束就没有了。每一次方法调用,都是独立的

6.4 包

1、包的作用:

(1)可以避免类重名

有了包之后,类的全名称就变为:包.类名

(2)分类组织管理众多的类

例如:java.lang包,java.util包,java.io包…

(3)可以控制某些类型或成员的可见范围

如果某个类型或者成员的权限修饰缺省的话,那么就仅限于本包使用

2、声明包的语法格式:

package 包名;

注意:

(1)必须在源文件的代码首行

(2)一个源文件只能有一个

3、包的命名规范和习惯:
(1)所有单词都小写,每一个单词之间使用.分割
(2)习惯用公司的域名倒置

例如:com.atguigu.xxx;

建议大家取包名时不要使用“java.xx"包

4、使用其他包的类:

前提:被使用的类或成员的权限修饰符是>缺省的

(1)使用类型的全名称

例如:java.util.Scanner input = new java.util.Scanner(System.in);

(2)使用import 语句之后,代码中使用简名称

5、import语句

import.类名;
import.*;

注意:当使用两个不同包的同名类时,例如:java.util.Date和java.sql.Date。

一个使用全名称,一个使用简名称

示例代码:

package com.atguigu.test;

import java.util.Scanner;

public class Test{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
    }
}

6.5 eclipse的使用

1、eclipse管理项目和代码的结构

workspace --> project --> 包–>类…

一个工作空间可以有多个项目。

2、快捷键

常规快捷键:

Ctrl + S:保存

Ctrl + C:复制

Ctrl + V:粘贴

Ctrl + X:剪切

Ctrl + Y:反撤销

Ctrl + Z:撤销

Ctrl + A:全选

eclipse中默认的快捷键:

Ctrl + 1:快速修复

Alt + /:代码提示

Alt + ?: Alt + Shift + / 方法的形参列表提示

Ctrl + D:删除选中行

Ctrl + Alt + ↓:向下复制行

Ctrl + Alt + ↑:向上复制行

Alt + ↓:与下面的行交换位置

Alt + ↑:与下面的行交换位置

Ctrl + Shift + F:快速格式

Ctrl + /:单行注释,再按一次取消

Ctrl + Shift + /:多行注释

Ctrl + Shift +\:取消多行注释

Shift + 回车:在光标下一行插入新航开始编辑

Ctrl + Shift + 回车:在光标上一行插入新航开始编辑

Alt + Shift + A:多行编辑 再按一次退出多行编辑模式

Alt + Shift + S:弹出自动生成代码的菜单选择,包括自动生成构造器、get/set、equals…

Ctrl + Shift + O:快速导包

Ctrl + Shift + T:打开某个类的源文件

Ctrl + O:打开某个类型的摘要outline

3、快速开发的代码模板

代码模板 + Alt + /

(1)main

public static void main(String[] args){

}

(2)sysout

System.out.println();

(3)for

for(int i=0; i<数组名.lenght; i++){

}

其他详细使用见《JavaSE_柴林燕_相关工具.docx》

6.6 面向对象的基本特征之二:继承

1、为什么要继承?继承的好处?(理解)

(1)代码的复用

(2)代码的扩展

2、如何实现继承?

语法格式:

【修饰符】 class 子类  extends 父类{
    
}

3、继承的特点

(1)子类会继承父类的所有特征(属性、方法)

但是,私有的在子类中是不能直接使用的

(2)子类不会继承父类的构造器

因为,父类的构造器是用于创建父类的对象的

(3)子类的构造器中又必须去调用父类的构造器

在创建子类对象的同时,为从父类继承的属性进行初始化用,可以借助父类的构造器中的代码为属性赋值。

(4)Java只支持单继承:一个子类只能有一个“直接”父类

(5)Java又支持多层继承:父类还可以有父类,特征会代代相传

(6)一个父类可以同时拥有很多个子类

6.7 关键字super

super关键字:引用父类的,找父类的xx

用法:

(1)super.属性

当子类声明了和父类同名的成员变量时,那么如果要表示某个成员变量是父类的,那么可以加“super.”

(2)super.方法

当子类重写了父类的方法,又需要在子类中调用父类被重写的方法,可以使用"super."

(3)super()或super(实参列表)

super():表示调用父类的无参构造

super(实参列表):表示调用父类的有参构造

注意:

(1)如果要写super()或super(实参列表),必须写在子类构造器的首行

(2)如果子类的构造器中没有写:super()或super(实参列表),那么默认会有 super()

(3)如果父类没有无参构造,那么在子类的构造器的首行“必须”写super(实参列表)

6.8 方法的重写

1、方法的重写(Override)

当子类继承了父类的方法时,又觉得父类的方法体的实现不适合于子类,那么子类可以选择进行重写。

2、方法的重写的要求

(1)方法名:必须相同

(2)形参列表:必须相同

(3)修饰符

​ 权限修饰符: >=

(4)返回值类型

​ 如果是基本数据类型和void:必须相同

​ 如果是引用数据类型:<=

​ 在Java中我们认为,在概念范围上:子类 <父类

3、重载(Overload)与重写(Override)的区别

​ 重载(Overload):在同一个类中,方法名相同,形参列表不同,和返回值类型无关的两个或多个方法。

​ 重写(Override):在父子类之间。对方法签名的要求见上面。

特殊的重载:

public class TestOverload {
	public static void main(String[] args) {
		B b = new B();
		//b对象可以调用几个a方法
		b.a();
		b.a("");//从b对象同时拥有两个方法名相同,形参不同的角度来说,算是重载
	}
}
class A{
	public void a(){
		//...
	}
}
class B extends A{
	public void a(String str){
		
	}
}

6.9 非静态代码块

1、语法格式

【修饰符】 class 类名{
    {
        非静态代码块
    }
}

2、作用

目的:在创建的过程中,为对象属性赋值,协助完成实例初始化的过程

3、什么时候执行?

(1)每次创建对象时都会执行

(2)优先于构造器执行

6.10 实例初始化过程

1、概念描述

  • 实例初始化过程:实例对象创建的过程

  • 实例初始化方法:实例对象创建时要执行的方法

  • 实例初始化方法的由来:它是有编译器编译生成的

  • 实例初始化方法的形式:()或(形参列表)

  • 实例初始化方法的构成:

    ①属性的显式赋值代码

    ②非静态代码块的代码

    ③构造器的代码

    其中

    ①和②按顺序执行,从上往下

    ③在①和②的后面

因此一个类有几个构造器,就有几个实例初始化方法。

2、单个类实例初始化方法

示例代码:

class Demo{
	{
		System.out.println("非静态代码块1");
	}
	
	private String str = assign();//调用方法,来为str进行显式赋值
	
	public Demo(){
		System.out.println("无参构造");
	}
	public Demo(String str){
		this.str = str;
		System.out.println("有参构造");
	}
	
	{
		System.out.println("非静态代码块2");
	}
	
	public String assign(){
		System.out.println("assign方法");
		return "hello";
	}
}

图解:

3、父子类的实例初始化

注意:

(1)原先super()和super(实参列表)说是调用父类的构造器,现在就要纠正为调用父类的实例初始化方法了

(2)原先super()和super(实参列表)说是必须在子类构造器的首行,现在要纠正为必须在子类实例初始化方法的首行

结论:

(1)执行顺序是先父类实例初始化方法,再子类实例初始化方法

(2)如果子类重写了方法,通过子类对象调用,一定是执行重写过的方法

示例代码:

class Ba{
	private String str = assign();
	{
		System.out.println("(1)父类的非静态代码块");
	}
	public Ba(){
		System.out.println("(2)父类的无参构造");
	}
	public String assign(){
		System.out.println("(3)父类的assign()");
		return "ba";
	}
}
class Er extends Ba{
	private String str = assign();
	{
		System.out.println("(4)子类的非静态代码块");
	}
	public Er(){
		//super()  ==>调用父类的实例初始化方法,而且它在子类实例初始化方法的首行
		System.out.println("(5)子类的无参构造");
	}
	
	public String assign(){
		System.out.println("(6)子类的assign()");
		return "er";
	}
}
class Test{
    public static void main(String[] args){
        new Er();//612645
    }
}

图解:

6.11 面向对象的基本特征之三:多态

1、多态:

语法格式:

父类 引用/变量 = 子类的对象;

2、前提:

(1)继承

(2)方法的重写

(3)多态引用

3、现象:

​ 编译时看左边/“父类”,运行时看右边/“子类”。

​ 编译时,因为按父类编译,那么只能父类有的方法,子类扩展的方法是无法调用的;

​ 执行时一定是运行子类重写的过的方法体。

示例代码:

class Person{
	public void eat(){
		System.out.println("吃饭");
	}
	public void walk(){
		System.out.println("走路");
	}
}
class Woman extends Person{
	public void eat(){
		System.out.println("细嚼慢咽的吃饭");
	}
	public void walk(){
		System.out.println("婀娜多姿走路");
	}
	public void shop(){
		System.out.println("买买买...");
	}
}
class Man extends Person{
	public void eat(){
		System.out.println("狼吞虎咽的吃饭");
	}
	public void walk(){
		System.out.println("大摇大摆的走路");
	}
	public void smoke(){
		System.out.println("吞云吐雾");
	}
}
class Test{
    public static void main(String[] args){
        Person p = new Woman();//多态引用
        p.eat();//执行子类重写
        p.walk();//执行子类重写
        //p.shop();//无法调用
    }
}

4、应用:

(1)多态参数:形参是父类,实参是子类对象

(2)多态数组:数组元素类型是父类,元素存储的是子类对象

示例代码:多态参数

class Test{
    public static void main(String[] args){
        test(new Woman());//实参是子类对象
        test(new Man());//实参是子类对象
    }
    public static void test(Person p){//形参是父类类型
        p.eat();
        p.walk();
    }
}

示例代码:多态数组

class Test{
    public static void main(String[] args){
        Person[] arr = new Person[2];//多态数组
        arr[0] = new Woman();
        arr[1] = new Man();
        
        for(int i=0; i<arr.length; i++){
            all[i].eat();
            all[i].walk();
        }
    }
}

5、向上转型与向下转型:父子类之间的转换

(1)向上转型:自动类型转换

​ 当把子类的对象赋值给父类的变量时(即多态引用时),在编译时,这个对象就向上转型为父类。此时就看不见子类“特有、扩展”的方法。

(2)向下转型:强制转换。有风险,可能会报ClassCastException异常。

​ 当需要把父类的变量赋值给一个子类的变量时,就需要向下转型。

要想转型成功,必须保证该变量中保存的对象的运行时类型是<=强转的类型

示例代码:

class Person{
	//方法代码省略...
}
class Woman extends Person{
    //方法代码省略...
}
class ChineseWoman extends Woman{
	//方法代码省略...
}
 public class Test{
     public static void main(String[] args){
		//向上转型
		Person p1 = new Woman();
		//向下转型
		Woman m = (Woman)p1; 
		//p1变量中实际存储的对象就是Woman类型,和强转的Woman类型一样

		//向上转型
		Person p2 = new ChineseWoman();
		//向下转型
		Woman w2 = (Woman) p2; 
		//p2变量中实际存储的对象是ChineseWoman类型,强制的类型是Woman,ChineseWoman<Woman类型     
     }
 }

6、instanceof

表达式语法格式:

对象/变量  instanceof  类型

运算结果:true 或 false

作用:

用来判断这个对象是否属于这个类型,或者说,是否是这个类型的对象或这个类型子类的对象

示例代码:

class Person{
	//方法代码省略...
}
class Woman extends Person{
    //方法代码省略...
}
class ChineseWoman extends Woman{
	//方法代码省略...
}
 public class Test{
     public static void main(String[] args){
         Person p = new Person();
         Woman w = new Woman();
         ChineseWoman c = new ChineseWoman();
         
         if(p instanceof Woman){//false
             
         }
         if(w instanceof Woman){//true
             
         }
         if(c instanceof Woman){//true
             
         }
     }
 }

第七章 面向对象的高级特性

修饰符的学习围绕三个问题:

(1)单词的意思

(2)可以修饰什么?

(3)用它修饰后有什么不同?

7.1 关键字:final

final:最终的

用法:

(1)修饰类(包括外部类、内部类类)

表示这个类不能被继承,没有子类

(2)修饰方法

表示这个方法不能被重写

(3)修饰变量(成员变量(类变量、实例变量),局部变量)

表示这个变量的值不能被修改

注意:如果某个成员变量用final修饰后,也得手动赋值,而且这个值一旦赋完,就不能修改了,即没有set方法

7.2 关键字:native

native:本地的,原生的
用法:

​ 只能修饰方法

​ 表示这个方法的方法体代码不是用Java语言实现的。

​ 但是对于Java程序员来说,可以当做Java的方法一样去正常调用它,或者子类重写它。

JVM内存的管理:

方法区:类的信息、常量、静态变量、动态编译生成的字节码信息

虚拟机栈:Java语言实现的方法的局部变量

本地方法栈:非Java语言实现的方法的局部变量,即native方法执行时的内存区域

堆:new出来的对象

程序计数器:记录每一个线程目前执行到哪一句指令

7.3 关键字:static

static:静态的

用法:

1、成员方法:我们一般称为静态方法或类方法

(1)不能被重写

(2)被使用

本类中:其他方法中可以直接使用它

其他类中:可以使用“类名.方法"进行调用,也可以使用"对象名.方法",推荐使用“类名.方法"

(3)在静态方法中,我们不能出现:this,super,非静态的成员

2、成员变量:我们一般称为静态变量或类变量

(1)静态变量的值是该类所有对象共享的

(2)静态变量存储在方法区

(3)静态变量对应的get/set也是静态的

(4)静态变量与局部变量同名时,就可以使用“类名.静态变量"进行区分

3、内部类:后面讲

4、代码块:静态代码块

5、静态导入(JDK1.5引入)

没有静态导入

package com.atguigu.utils;

public class Utils{
    public static final int MAX_VALUE = 1000;
    public static void test(){
        //...
    }
}


package com.atguigu.test;

import com.atguigu.utils;

public class Test{
    public static void main(String[] args){
        System.out.println(Utils.MAX_VALUE);
        Utils.test();
    }
}

使用静态导入

package com.atguigu.utils;

public class Utils{
    public static final int MAX_VALUE = 1000;
    public static void test(){
        //...
    }
}
package com.atguigu.test;

import static com.atguigu.utils.Utils.*;

public class Test{
    public static void main(String[] args){
        System.out.println(MAX_VALUE);
        test();
    }
}

7.4 静态代码块

1、语法格式:

【修饰符】 class 类名{
    static{
        静态代码块;
    }
}

2、作用:

协助完成类初始化,可以为类变量赋值。

3、类初始化()

类的初始化有:

①静态变量的显式赋值代码

②静态代码块中代码

其中①和②按顺序执行

注意:类初始化方法,一个类只有一个

4、类的初始化的执行特点:

(1)每一个类的()只执行一次

(2)如果一个子类在初始化时,发现父类也没有初始化,会先初始化父类

(3)如果既要类初始化又要实例化初始化,那么一定是先完成类初始化的

7.5 变量的分类与区别

1、变量按照数据类型分:

(1)基本数据类型的变量,里面存储数据值

(2)引用数据类型的变量,里面存储对象的地址值

int a = 10;//a中存储的是数据值

Student stu = new Student();//stu存储的是对象的地址值
int[] arr = new int[5];//arr存储的是数组对象的地址值
String str = "hello";//str存储的是"hello"对象的地址值

2、变量按照声明的位置不同:

(1)成员变量

(2)局部变量

3、成员变量与局部变量的区别

(1)声明的位置不同

成员变量:类中方法外

局部变量:(1)方法的()中,即形参(2)方法体的{}的局部变量(3)代码块{}中

(2)存储的位置不同

成员变量:

​ 如果是静态变量(类变量),在方法区中

​ 如果是非静态的变量(实例变量),在堆中

局部变量:栈

(3)修饰符不同

成员变量:4种权限修饰符、static、final。。。。

局部变量:只有final

(4)生命周期

成员变量:

​ 如果是静态变量(类变量),和类相同

​ 如果是非静态的变量(实例变量),和所属的对象相同,每一个对象是独立

局部变量:每次执行都是新的

(5)作用域

成员变量:

​ 如果是静态变量(类变量),在本类中随便用,在其他类中使用“类名.静态变量"

​ 如果是非静态的变量(实例变量),在本类中只能在非静态成员中使用,在其他类中使用“对象名.非静态的变量"

局部变量:有作用域

7.7 根父类

1、java.lang.Object类是类层次结构的根父类。包括数组对象。

(1)Object类中声明的所有的方法都会被继承到子类中,那么即所有的对象,都拥有Object类中的方法

(2)每一个对象的创建,最终都会调用到Object实例初始化方法()

(3)Object类型变量、形参、数组,可以存储任意类型的对象

2、Object类的常用方法

(1)public String toString():

①默认情况下,返回的是“对象的运行时类型 @ 对象的hashCode值的十六进制形式"

②通常是建议重写,如果在eclipse中,可以用Alt +Shift + S–>Generate toString()

③如果我们直接System.out.println(对象),默认会自动调用这个对象的toString()

(2)public final Class<?> getClass():获取对象的运行时类型

(3)protected void finalize():当对象被GC确定为要被回收的垃圾,在回收之前由GC帮你调用这个方法。而且这个方法只会被调用一次。子类可以选择重写。

(4)public int hashCode():返回每个对象的hash值。

规定:①如果两个对象的hash值是不同的,那么这两个对象一定不相等;

​ ②如果两个对象的hash值是相同的,那么这两个对象不一定相等。

主要用于后面当对象存储到哈希表等容中时,为了提高性能用的。

(5)public boolean equals(Object obj):用于判断当前对象this与指定对象obj是否“相等”

①默认情况下,equals方法的实现等价于与“==”,比较的是对象的地址值

②我们可以选择重写,重写有些要求:

A:如果重写equals,那么一定要一起重写hashCode()方法,因为规定:

​ a:如果两个对象调用equals返回true,那么要求这两个对象的hashCode值一定是相等的;

​ b:如果两个对象的hashCode值不同的,那么要求这个两个对象调用equals方法一定是false;

​ c:如果两个对象的hashCode值相同的,那么这个两个对象调用equals可能是true,也可能是false

B:如果重写equals,那么一定要遵循如下几个原则:

​ a:自反性:x.equals(x)返回true

​ b:传递性:x.equals(y)为true, y.equals(z)为true,然后x.equals(z)也应该为true

​ c:一致性:只要参与equals比较的属性值没有修改,那么无论何时调用结果应该一致

​ d:对称性:x.equals(y)与y.equals(x)结果应该一样

​ e:非空对象与null的equals一定是false

7.8 关键字:abstract

1、什么时候会用到抽象方法和抽象类?

当声明父类的时候,在父类中某些方法的方法体的实现不能确定,只能由子类决定。但是父类中又要体现子类的共同的特征,即它要包含这个方法,为了统一管理各种子类的对象,即为了多态的应用。

那么此时,就可以选择把这样的方法声明为抽象方法。如果一个类包含了抽象方法,那么这个类就必须是个抽象类。

2、抽象类的语法格式

【权限修饰符】 abstract class 类名{
    
}
【权限修饰符】 abstract class 类名 extends 父类{
    
}

3、抽象方法的语法格式

【其他修饰符】 abstract 返回值类型  方法名(【形参列表】);

抽象方法没有方法体

4、抽象类的特点

(1)抽象类不能直接实例化,即不能直接new对象

(2)抽象类就是用来被继承的,那么子类继承了抽象类后,必须重写所有的抽象方法,否则这个子类也得是抽象类

(3)抽象类也有构造器,这个构造的作用不是创建抽象类自己的对象用的,给子类在实例化过程中调用;

(4)抽象类也可以没有抽象方法,那么目的是不让你创建对象,让你创建它子类的对象

(5)抽象类的变量与它子类的对象也构成多态引用

5、不能和abstract一起使用的修饰符?

(1)final:和final不能一起修饰方法和类

(2)static:和static不能一起修饰方法

(3)native:和native不能一起修饰方法

(4)private:和private不能一起修饰方法

7.9 接口

1、接口的概念

接口是一种标准。注意关注行为标准(即方法)。

面向对象的开发原则中有一条:面向接口编程。

2、接口的声明格式

【修饰符】 interface 接口名{
    接口的成员列表;
}

3、类实现接口的格式

【修饰符】 class 实现类  implements 父接口们{
    
}

【修饰符】 class 实现类 extends 父类 implements 父接口们{
    
}

4、接口继承接口的格式

【修饰符】 interface 接口名 extends 父接口们{
    接口的成员列表;
}

5、接口的特点

(1)接口不能直接实例化,即不能直接new对象

(2)只能创建接的实现类对象,那么接口与它的实现类对象之间可以构成多态引用。

(3)实现类在实现接口时,必须重写所有抽象的方法,否则这个实现类也得是抽象类。

(4)Java规定类与类之间,只能是单继承,但是Java的类与接口之间是多实现的关系,即一个类可以同时实现多个接口

(5)Java还支持接口与接口之间的多继承。

6、接口的成员

JDK1.8之前:

(1)全局的静态的常量:public static final,这些修饰符可以省略

(2)公共的抽象方法:public abstract,这些修饰符也可以省略

JDK1.8之后:

(3)公共的静态的方法:public static ,这个就不能省略了

(4)公共的默认的方法:public default,这个就不能省略了

7、默认方法冲突问题

(1) 当一个实现类同时实现了两个或多个接口,这个多个接口的默认方法的签名相同。

解决方案:

方案一:选择保留其中一个

接口名.super.方法名(【实参列表】);

方案二:完全重写

(2)当一个实现类同时继承父类,又实现接口,父类中有一个方法与接口的默认方法签名相同

解决方案:

方案一:默认方案,保留父类的

方案二:选择保留接口的

接口名.super.方法名(【实参列表】);

方案三:完全重写

8、示例代码

public interface Flyable{
	long MAX_SPEED = 7900000;
    void fly();
}
public class Bird implements Flyable{
    public void fly(){
        //....
    }
}

9、常用的接口

(1)java.lang.Comparable接口:自然排序

​ 抽象方法:int compareTo(Object obj)

(2)java.util.Comparator接口:定制排序

​ 抽象方法:int compare(Object obj1 ,Object obj2)

(3)示例代码

如果员工类型,默认顺序,自然顺序是按照编号升序排列,那么就实现Comparable接口

class Employee implements Comparable{
    private int id;
    private String name;
    private double salary;
    
    //省略了构造器,get/set,toString
    
    @Override
    public int compareTo(Object obj){
        return id - ((Employee)obj).id;
    }
}

如果在后面又发现有新的需求,想要按照薪资排序,那么只能选择用定制排序,实现Comparator接口

class SalaryComparator implements Comparator{
    public int compare(Object o1, Object o2){
        Employee e1 = (Employee)o1;
        Employee e2 = (Employee)o2;
        if(e1.getSalary() > e2.getSalary()){
            return 1;
        }else if(e1.getSalary() < e2.getSalary()){
            return -1;
        }
        return 0;
    }
}

7.10 内部类

1、内部类的概念

声明在另外一个类里面的类就是内部类。

2、内部类的4种形式

(1)静态内部类

(2)非静态成员内部类

(3)有名字的局部内部类

(4)匿名内部类

7.10.1 匿名内部类

1、语法格式:

//在匿名子类中调用父类的无参构造
new 父类(){
    内部类的成员列表
}

//在匿名子类中调用父类的有参构造
new 父类(实参列表){
    内部类的成员列表
}

//接口没有构造器,那么这里表示匿名子类调用自己的无参构造,调用默认父类Object的无参构造
new 父接口名(){
    
}

2、匿名内部类、匿名对象的区别?

System.out.println(new Student("张三"));//匿名对象

Student stu = new Student("张三");//这个对象有名字,stu

//既有匿名内部类,又是一个匿名的对象
new Object(){
    public void test(){
        .....
    }
}.test();

//这个匿名内部类的对象,使用obj这个名字引用它,既对象有名字,但是这个Object的子类没有名字
Object obj = new Object(){
    public void test(){
        .....
    }
};

3、使用的形式

(1)示例代码:继承式

abstract class Father{
    public abstract void test();
}
class Test{
    public static void main(String[] args){
        //用父类与匿名内部类的对象构成多态引用
        Father f = new Father(){
            public void test(){
                System.out.println("用匿名内部类继承了Father这个抽象类,重写了test抽象方法")
            }
        };
        f.test();
    }
}

(2)示例代码:实现式

interface Flyable{
    void fly();
}
class Test{
    public static void main(String[] args){
        //用父接口与匿名内部类的对象构成了多态引用
        Flyable f = new Flyable(){
            public void fly(){
                System.out.println("用匿名内部类实现了Flyable这个接口,重写了抽象方法");
            }
        };
        f.fly();
    }
}

(3)示例代码:用匿名内部类的匿名对象直接调用方法

new Object(){
    public void test(){
        System.out.println("用匿名内部类的匿名对象直接调用方法")
    }
}.test();

(4)示例代码:用匿名内部类的匿名对象直接作为实参

Student[] all = new Student[3];
all[0] = new Student("张三",23);
all[1] = new Student("李四",22);
all[2] = new Student("王五",20);

//用匿名内部类的匿名对象直接作为实参
//这个匿名内部类实现了Comparator接口
//这个匿名内部类的对象,是定制比较器的对象
Arrays.sort(all, new Comparator(){
    public int compare(Obeject o1, Object o2){
        Student s1 = (Student)o1;
        Student s2 = (Student)o2;
        return s1.getAge() - s2.getAge();
    }
});

7.10.2 静态内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{
	【其他修饰符】 static class  静态内部类 【extends 静态内部类自己的父类】 【implements 静态内部类的父接口们】{
        静态内部类的成员列表;
	}
	
	外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求:

​ 可以包含类的所有成员

(2)修饰符要求:

  • ​ 权限修饰符:4种
  • ​ 其他修饰符:abstract、final

(3)使用外部类的成员上是否有要求

  • ​ 只能使用外部类的静态成员

(4)在外部类中使用静态内部类是否有要求

  • ​ 正常使用

(5)在外部类的外面使用静态内部类是否有要求

1)如果使用的是静态内部类的静态成员
		外部类名.静态内部类名.静态成员
(2)如果使用的是静态内部类的非静态成员
		①先创建静态内部类的对象
		外部类名.静态内部类名 对象名 = new 外部类名.静态内部类名(【实参列表】);
		②通过对象调用非静态成员
		对象名.xxx

(6)字节码文件形式:外部类名$静态内部类名.class

3、示例代码

class Outer{
    private static int i = 10;
    static class Inner{
        public void method(){
            //...
            System.out.println(i);//可以
        }
        public static void test(){
            //...
            System.out.println(i);//可以
        }
    }
    
    public void outMethod(){
        Inner in = new Inner();
        in.method();
    }
    public static void outTest(){
        Inner in = new Inner();
        in.method();
    }
}
class Test{
    public static void main(String[] args){
        Outer.Inner.test();
        
        Outer.Inner in = new Outer.Inner();
        in.method();
    }
}

7.10.3 非静态内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{
	【修饰符】 class  非静态内部类 【extends 非静态内部类自己的父类】 【implements 非静态内部类的父接口们】{
        非静态内部类的成员列表;
	}
	
	外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求:

​ 不允许出现静态的成员

(2)修饰符要求

​ 权限修饰符:4种

​ 其他修饰符:abstract,final

(3)使用外部类的成员上是否有要求

​ 都可以使用

(4)在外部类中使用非静态内部类是否有要求

​ 在外部类的静态成员中不能使用非静态内部类

(5)在外部类的外面使用非静态内部类是否有要求

//使用非静态内部类的非静态成员
//(1)创建外部类的对象
外部类名  对象名1 = new  外部类名(【实参列表】);

//(2)通过外部类的对象去创建或获取非静态内部类的对象
//创建
外部类名.非静态内部类名  对象名2 = 对象名1.new 非静态内部类名(【实参列表】);

//获取
外部类名.非静态内部类名  对象名2 = 对象名1.get非静态内部类对象的方法(【实参列表】);

//(3)通过非静态内部类调用它的非静态成员
对象名2.xxx

(6)字节码文件形式:外部类名$非静态内部类名.class

3、示例代码

class Outer{
    private static int i = 10;
    private int j = 20;
    class Inner{
        public void method(){
            //...
            System.out.println(i);//可以
            System.out.println(j);//可以
        }
    }
    
    public void outMethod(){
        Inner in = new Inner();
        in.method();
    }
    public static void outTest(){
       // Inner in = new Inner();//不可以
    }
    
    public Inner getInner(){
        return new Inner();
    }
}
class Test{
    public static void main(String[] args){
        Outer out = new Outer();
        
        Outer.Inner in1 = out.new Inner();     //创建   	
        in1.method();
        
        Outer.Inner in2 = out.getInner();	//获取
        in2.method();
    }
}

7.10.4 局部内部类

1、语法格式

【修饰符】 class 外部类名  【extends 外部类的父类】 【implements 外部类的父接口们】{
	【修饰符】 返回值类型  方法名(【形参列表】){
        【修饰符】 class  局部内部类 【extends 局部内部类自己的父类】 【implements 局部内部类的父接口们】{
        	局部内部类的成员列表;
		}
	}	
	外部类的其他成员列表;
}

2、 使用注意事项

(1)包含成员是否有要求

​ 不允许出现静态的成员

(2)修饰符要求

​ 权限修饰符:不能

​ 其他修饰符:abstract、final

(3)使用外部类的成员等上是否有要求

​ ①使用外部类的静态成员:随便用

​ ②使用外部类的非静态成员:能不能用要看所在的方法是否是静态的

​ ③使用所在方法的局部变量:必须 final修饰的

(4)在外部类中使用局部内部类是否有要求

​ 有作用域

(5)在外部类的外面使用局部内部类是否有要求

​ 没法使用

(6)字节码文件形式:外部类名$编号局部内部类名.class

3、示例代码

class Outer{
    private static int i = 10;
    private int j = 20;

    
    public void outMethod(){
        class Inner{
            public void method(){
                //...
                System.out.println(i);//可以
                System.out.println(j);//可以
            }
   		}
        Inner in = new Inner();
        in.method();
    }
    public static void outTest(){
        final int k = 30;
       class Inner{
            public void method(){
                //...
                System.out.println(i);//可以
                System.out.println(j);//不可以
                System.out.println(k);//可以
            }
   		}
        Inner in = new Inner();
        in.method();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值