面向对象编程JAVA(高级部分)"/>
面向对象编程JAVA(高级部分)
文章目录
- 类变量和类方法
- 类变量
- 类方法/静态方法
- main方法
- main动态传值
- 代码块/初始化块
- 语法+概念
- 代码块的好处:
- 代码块细节
- 类什么时候被加载?*
- 创建一个对象时,在一个类调用顺序(重点、难点)
- 创建一个子类时(继承关系),他们的静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序(难点、面试题)
- 练习
- 单例设计模式
- 设计模式的概念
- 单例模式
- 单例模式饿汉类
- 单例模式懒汉类
- 饿汉式与懒汉式区别
- final关键字
- 应用场景
- 使用细节
- 抽象类
- 概念
- 介绍
- 使用细节
- 抽象——模板设计模式
- 接口
- 快速入门
- 基本介绍
- 应用场景
- 使用细节
- 接口VS继承
- 接口多态特性
- 多态参数
- 多态数组
- 多态传递
- 练习
- 内部类(非常重要、难点)
- 局部内部类
- 匿名内部类(重点、难点)
- 演示基于接口匿名内部类的使用
- 演示基于类匿名内部类的使用
- 使用细节
- 匿名内部类的最佳实践
- 练习一
- 练习二
- 成员内部类
- 静态内部类
类变量和类方法
类变量
也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
JDK8
之前,静态变量存储在常量池中;JDK8
以后,保存在class
实例的尾部,而class
对象存在于堆中。
public class ChildGame {public static void main(String [] args) {int count = 0;Child child1 = new Child("白骨精");child1.join();child1.count++;Child child2 = new Child("狐狸精");child2.join();child2.count++;Child child3 = new Child("老鼠精");child3.join();child3.count++;//类变量可以通过类名来访问System.out.println("共有" + Child.count + "个小孩加入了游戏!");//白骨精加入了游戏!//狐狸精加入了游戏!//老鼠精加入了游戏!//共有3个小孩加入了游戏!System.out.println("==========================================");System.out.println("child1.count=" + child1.count);System.out.println("child2.count=" + child2.count); System.out.println("child3.count=" + child3.count);//child1.count=3//child2.count=3//child3.count=3 }
}
class Child {private String name;//定义一个变量count,是一个类变量(静态变量)static//该变量最大的特点是会被child类的所有对象实例共享。public static int count = 0;public Child(String name) {this.name = name;}public void join() {System.out.println(name + "加入了游戏!");}
}
- 语法:访问修饰符
static
数据类型 变量名 - 如何访问?
- 对象名.类变量名【静态变量的访问修饰符的访问权限和范围和普通属性是一样的】
- 类名.类变量名(推荐使用)
public class VisitStatic {public static void main(String [] args) {//类名.类变量名//说明:类变量是随着类的加载而创建的,所以即使没有创建对象实例也可以访问。System.out.println(A.name);//对象名.类变量名A a = new A();System.out.println(a.name);}
}
class A {//类变量//类变量的访问必须遵守相关的访问权限,这里是publicpublic static String name = "郭泳妍";
}============================================================================public class VisitStatic {public static void main(String [] args) {//类名.类变量名//说明:类变量是随着类的加载而创建的,所以即使没有创建对象实例也可以访问。System.out.println(A.getName());//对象名.类变量名A a = new A();System.out.println(a.getName());}
}
class A {//类变量//类变量的访问必须遵守相关的访问权限,这里是publicprivate static String name = "郭泳妍";public static String getName() {return name;}public static void setName(String name) {A.name = name;}
}
- 当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量。
- 类变量与实例变量的区别:类变量是该类的所有对象共享的,实例变量是每个对象独享的。
- 加上
static
称为类变量或者静态变量,否则称为实例变量/普通变量/非静态变量。 - 类变量在加载时就已经被初始化,意思是即使没有创建对象,只要类加载了就可以使用类变量。
- 类变量的生命周期是随着类的加载开始的,随着类的消亡而销毁。
类方法/静态方法
- 语法:访问修饰符
static
数据返回类型 方法名() { } - 如何调用?
- 类名.类方法.
- 对象名.类方法名【前提是满足访问修饰符的访问权限和范围】
public class StaicMethod {public static void main(String [] args) {//对象名.类方法名Stu tom = new Stu("tom");//tom.pay(1000);Stu.pay(1000); //两种方法均可以Stu mary = new Stu("mary");mary.pay(2000);//类名.类方法//输出当前收到的总学费Stu.show(); //总学费有3000.0}
}
class Stu {private String name;//定义一个静态变量来累计学生学费private static double fee = 0;public Stu() {super();}public Stu(String name) {super();this.name = name;}//当方法使用了static修饰后,该方法为静态方法public static void pay(double fee) {Stu.fee += fee; //累计到静态变量的fee}public static void show() {System.out.println("总学费有" + Stu.fee);}
}
- 经典使用场景:当方法中不涉及到任何和对象有关的成员,即不创建实例也可以调用某个方法(也就是说当成工具使用),则可以将方法设计成静态方法提升开发效率。
System.out.println("9开方的结果是:" + Math.sqrt(9));
- 开发自己的工具时,可以将方法做成静态的,方便进行调用。
- 类方法和普通方法都是随着类的加载而加载的,将结构信息存储在方法区中。
- 类方法中无
this
这个参数,而普通方法中隐含着this
这个参数。 - 类方法可以通过类名和对象名来调用。
- 普通方法和对象有关,需要通过对象名来调用,比如对象名.方法名(参数),不能通过类名调用。
- 类方法中不允许使用和对象有关的关键字,如
this、super
,而普通方法可以。 - 类方法中只能访问静态变量或者静态方法。
- 普通成员既可以访问普通变量,也可以访问静态变量。
- 静态方法只能访问静态的成员;非静态的方法,可以访问静态成员和非静态成员。
class D {private int n1 = 100;private static int n2 = 200;public void say() {}public static void hi() {//类方法中不允许使用和对象有关的关键字,如`this、super//System.out.println(this.n1);}public static void hello() {//System.out.println(D.n1); ---->报错System.out.println(n2);System.out.println(D.n2);//System.out.println(this.n2); ---->报错hi();//say(); ---->报错}//静态方法只能访问静态的成员;非静态的方法,可以访问静态成员和非静态成员。public void OK() {//非静态成员System.out.println(n1);say();//静态成员System.out.println(n2);hi();hello();}
}
main方法
- 语法:
public static void main(String [] args)
Java
虚拟机需要调用类的main
方法,所以该方法的访问权限必须要是public
,否则运行不了。java
虚拟机在执行main
方法时不必创建对象,作为程序入口,所以该方法必须是static
。- 该方法接收
Sting
类型的数组参数,该数组中保存执行java
命令时传递给所运行的类的参数。
- 在
main()
方法中,可以直接调用main
方法所在类的静态方法和静态属性。 - 但是,不能直接访问该类中的非静态成员,必须创建一个实例对象后才可以访问。
public class Main01 {//静态变量/属性private static String name = "郭泳妍";//静态方法public static void hi() {System.out.println("hi!");}//非静态属性private int n1 = 100;//非静态方法public void cry() {System.out.println("喵喵喵……");}public static void main(String[] args) {// TODO Auto-generated method stub//可以直接使用name,静态方法可以访问本类的静态成员System.out.println(name); //郭泳妍hi();//静态方法不可以访问本类的非静态成员和非静态方法//System.out.println(n1); ----->报错//cry(); ----->报错//要访问需要先创建实例对象再调用Main01 main01 = new Main01();main01.hi();main01.cry();}}
main动态传值
不同的编译器和不同的版本在网上查询教程。
代码块/初始化块
语法+概念
- 属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过
{ }
包围起来。 - 但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是记载类时,或者创建对象时隐式调用。
- 基本语法:
[修饰符] {
代码
};
注意:
- 修饰符可选,要写的话只能写
static
。 - 代码块分两类,使用
static
修饰的叫静态代码块,没有static
修饰的叫做普通代码块/非静态代码块。 - 逻辑语句可以分为任意逻辑语句。
;
可以写上,也可以省略。
代码块的好处:
- 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作。
- 应用场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码重用性。
- 三个构造器中都要有相同的输出语句,这时可以把相同的语句放入一个代码块中即可。
- 这样当我们不管调用哪个构造器,创建对象都会先调用代码块内容。
- 代码块调用的顺序优先于构造器。
public class CodeBlock01 {public static void main(String[] args) {// TODO Auto-generated method stubMovie movie1 = new Movie("你好李焕英");Movie movie2 = new Movie("你好李焕英","贾玲");}}
class Movie {private String name;private String director;private double price;//3个构造器 ------>重载//三个构造器中都要有相同的输出语句,这时可以把相同的语句放入一个代码块中即可。//这样当我们不管调用哪个构造器,创建对象都会先调用代码块内容。//代码块调用的顺序优先于构造器。{System.out.println("电影屏幕打开");System.out.println("广告开始");System.out.println("电影正式开始"); }public Movie(String name) {super();
// System.out.println("电影屏幕打开");
// System.out.println("广告开始");
// System.out.println("电影正式开始"); System.out.println("public Movie(String name)被调用");this.name = name;}public Movie(String name, String director) {super();this.name = name;this.director = director;System.out.println("public Movie(String name, String director)被调用");}public Movie(String name, String director, double price) {super();this.name = name;this.director = director;this.price = price;}
}============================================================================电影屏幕打开
广告开始
电影正式开始
public Movie(String name)被调用
电影屏幕打开
广告开始
电影正式开始
public Movie(String name, String director)被调用
代码块细节
static
代码块也叫静态代码块,作用就是对类进行初始化,而且他随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象就执行一次。
类什么时候被加载?*
- 创建对象时实例化(new)。
- 创建子类对象实例,父类也会被加载。
- 使用类的静态成员时(静态属性,静态方法)
普通的代码块,在创建对象实例时被隐式调用,对象被创建一次,就会被调用一次;而如果只是使用类的静态成员时,普通代码块并不会执行。
public class CodeBlock02 {public static void main(String[] args) {// TODO Auto-generated method stub//创建对象实例时(new)AA a1 = new AA(); //AA的静态代码块被执行!//创建子类对象实例,父类也会被加载。//先加载父类,再加载子类AA a2 = new AA(); //BB的静态代码块被执行! AA的静态代码块被执行!//使用类的静态成员时(静态属性,静态方法)//调用静态成员时,只有static代码块会被执行,普通代码块不会被执行System.out.println(Cat.n1); //Cat的第一次静态代码块被执行! Cat的第三次静态代码块被执行! 100//即使创建对象,static静态代码块只会被执行一次DD dd1 = new DD();DD dd2 = new DD(); //DD的静态代码块被执行!}}
class DD {static {System.out.println("DD的静态代码块被执行!");}
}
class Cat {public static int n1 = 100;static {System.out.println("Cat的第一次静态代码块被执行!");}{System.out.println("Cat的第二次静态代码块被执行!");}static {System.out.println("Cat的第三次静态代码块被执行!");}
}
class BB {static {System.out.println("BB的静态代码块被执行!");}
}
class AA extends BB{//静态代码块static {System.out.println("AA的静态代码块被执行!");}
}
创建一个对象时,在一个类调用顺序(重点、难点)
- 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化块调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们的定义顺序调用)。
- 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和普通属性初始化则按他们的定义顺序调用)
- 调用构造方法
public class CodeBlock03 {public static void main(String[] args) {// TODO Auto-generated method stub//1. 静态代码块和静态属性初始化块调用的优先级一样,//如果有多个静态代码块和多个静态变量初始化,则按他们的定义顺序调用//2. 普通代码块和普通属性初始化调用的优先级一样,//如果有多个普通代码块和普通属性初始化则按他们的定义顺序调用A a = new A(); //1.getN1()被调用…… 2. A 静态代码块被调用……//3.getN2()被调用…… 4.A 普通代码块被调用……//5.A构造器被调用… }}
class A {public A() {System.out.println("A构造器被调用……");}//普通属性初始化private int n2 = getN2();//普通代码块{System.out.println("A 普通代码块被调用……");}//静态属性初始化private static int n1 = getN1();//静态代码块static {System.out.println("A 静态代码块被调用……");}public static int getN1() {System.out.println("getN1()被调用……");return 100;}public int getN2() {System.out.println("getN2()被调用……");return 200;}
}============================================================================getN1()被调用……
A 静态代码块被调用……
getN2()被调用……
A 普通代码块被调用……
A构造器被调用……
构造器的最前面其实隐含了super()
和调用普通代码块,静态相关的代码块、属性初始化,在类加载时就执行完毕,因此是优先于构造器和普通代码块执行的。
public class CodeBlock04 {public static void main(String[] args) {// TODO Auto-generated method stubAAA a = new B(); //AAA普通代码块被调用 AAAA()构造器被调用、//BBB普通代码块被调用、 B()构造器被调用}}
class AAA {public AAA() {//隐藏的执行要求://1.super(); ---->继承讲解//2. 调用普通代码块System.out.println("AAA()构造器被调用");}{System.out.println("AAA普通代码块被调用");}
}
class B extends AAA{public B () {System.out.println("B()构造器被调用");}{System.out.println("BBB普通代码块被调用");}
}===================================================================================
AAA普通代码块被调用
AAA()构造器被调用
BBB普通代码块被调用
B()构造器被调用
创建一个子类时(继承关系),他们的静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序(难点、面试题)
- 父类的静态代码块和静态属性(优先级相同,按定义顺序调用)。
- 子类的静态代码块和静态属性(优先级相同,按定义顺序调用)。
- 父类的普通代码块和普通属性初始化(优先级相同,按定义顺序调用)。
- 父类的构造方法。
- 子类的普通代码块和普通属性初始化(优先级相同,按定义顺序调用)。
- 子类的构造方法。
练习
静态代码块可以只能调用静态成员,普通代码块可以调用任意成员。
单例设计模式
设计模式的概念
- 静态方法和属性的经典使用。
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。
单例模式
就是采用一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
单例模式饿汉类
类被加载后即全部对象都创建【创建后未被利用,可能造成资源浪费】
- 构造器私有化(防止直接
new
对象) - 类的内部创建对象。
- 向外暴露一个静态的公共方法。
- 代码实现。
/** 构造器私有化(防止直接`new`对象)* 类的内部创建对象。* 向外暴露一个静态的公共方法。* 代码实现。*///单例模式保证一个类只能创建一个对象
public class TestSingleton {public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2); //true}}
class Singleton {//本类类型的静态私有变量//为了能够在静态方法中将私有的inatance返回,需将其修饰为staticprivate static Singleton instance;//构造器私有化private Singleton() {System.out.println("Singleton()无参构造器被调用……");}//静态的公共方法public static Singleton getInstance() {if(instance == null) {//类的内部创建对象。instance = new Singleton();}return instance;}
}===================================================================================Singleton()无参构造器被调用……
true
单例模式懒汉类
即类中的对象被使用后才会被创建
//懒汉式public class TestSingleton02 {public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println(Cat.n1); // 100【构造器未被调用】Cat instance1 = Cat.getInstance();System.out.println(instance1); //构造器被调用 Cat [name=小可爱]Cat instance2 = Cat.getInstance();System.out.println(instance2); //Cat [name=小可爱],cat已经不是空System.out.println(instance1 == instance2); //true}}//希望程序在运行过程中,只能创建一个cat对象
//使用单例模式
class Cat {private String name; public static int n1 = 100;private static Cat cat; //默认是nullprivate Cat(String name) {System.out.println("构造器被调用");this.name = name;}//懒汉式中,只有当用户使用getInstance() 时才会返回cat对象,//后面再次调用时返回上次创建的cat对象//从而保证了单例public static Cat getInstance() {if(cat == null) {cat = new Cat("小可爱");}return cat;}@Overridepublic String toString() {return "Cat [name=" + name + "]";}}===================================================================================100
构造器被调用
Cat [name=小可爱]
Cat [name=小可爱]
true
饿汉式与懒汉式区别
- 最主要的区别在于创建对象的时机不同:饿汉式是类加载时就创建了对象实例,而懒汉式是在使用时才创建。
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。
- 饿汉式存在浪费资源的可能。
final关键字
final可以修饰类、属性、方法、局部变量。
应用场景
- 当不希望类被继承时,可以用
final
修饰。 - 但不希望父类的某个方法被子类覆盖或者重写时,可以用
final
关键字修饰。
public final void hi(){}
- 当不希望类的某个属性的值被修改但是仍然可以调用。
public final double TAX = 0.09;
- 当不希望某个局部变量被修改。
public class Final01 {public static void main(String[] args) {// TODO Auto-generated method stub}}//当不希望A类被继承时,可以用final修饰A类。
final class A {}
//class B extends A {}class C {// 但不希望hi方法被子类覆盖或者重写时,可以用final关键字修饰hi方法。public void hi() {};//public final void hi(){}
}
class D extends C {@Overridepublic void hi() {// TODO Auto-generated method stubsuper.hi();System.out.println("重写了C类的hi方法");}}
使用细节
final
修饰的属性又叫常量,一般字母全部是大写,且字母用下划线隔开。如TAX_NUM
final
修饰的属性在定义时必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:
- 定义时:
public final double TAX_RATE = 0.08;
- 在构造器中。
- 在代码块中。
class AA {public final double TAX_RATE1 = 0.09; //定义时public final double TAX_RATE2;public final double TAX_RATE3;public AA () {TAX_RATE2 = 0.02; //在构造器中}{TAX_RATE3 = 0.01; //在代码块中}
}
- 如果
final
修饰的属性是静态的,则初始化的位置只能是:
- 定义时
- 在静态代码块中,不能再构造器中赋值
class BB {public static final double TAX_RATE1 = 0.09; //定义时public static final double TAX_RATE2;public final double TAX_RATE3;
// public static final double TAX_RATE3;public BB () {TAX_RATE3 = 0.01; //不能在构造器中}static {TAX_RATE2 = 0.02; //在静态代码块中}}
final
类不能继承,但是可以实例化对象。
CC cc = new CC()
- 如果类不是
final
类,但是含有final
方法,则该方法虽然不能重写,但是可以被继承,可以在主类中创建对象时调用。 - 一般来说,如果一个已经是
final
类,就没有必要将方法修饰成final
方法。 - final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理。
public class Final02 {public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println(DD.num1); //100System.out.println(DD.num2); //DD静态代码块被执行 200} }
class DD {public final static int num1 = 100;public static int num2 = 200;static {System.out.println("DD静态代码块被执行");}
}
- 包装类(
Interger、Double、Float、Boolean
等都是final
),String
类也是final
类,这些都布不能被继承。
抽象类
概念
- 当父类的某个方法需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。
- 当一个类中存在抽象方法时,需要将该类声明为
abstract
类。 - 一般来说抽象类会被继承,由其子类实现抽象方法。
介绍
- 用
abstract
修饰的类就是抽象类。
访问修饰符 abstract 类名 {
}
- 用
abstract
修饰的方法就是抽象方法。
访问修饰符
abstract
返回类型 方法名 (参数列表); //没有方法体
- 抽象类的价值更多在于设计,是设计师设计好后,让子类继承并实现抽象类。
- 抽象类在面试中常见。
使用细节
- 抽象类不能实例化。
- 抽象类不一定包含抽象方法,还可以有实现方法。
- 一旦类包含了
abstract
方法,则这个类必须声明为abstract
。 abstract
只能修饰类和方法,不能修饰属性和其他的。- 抽象类还是类,可以有任意成员,比如:非抽象方法、构造器、静态属性等等。
- 抽象方法不能有主体,即不能实现。【没有方法体】
- 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非他自己也声明为
abstract
类。【所谓实现方法,就是要有方法体】。 - 抽象方法不能使用
private、final、static
修饰,因为这些关键字都是和重写相违背的。
abstract class A {public abstract void hi();
}
//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非他自己也声明为abstract类。
abstract class B extends A {}
class C extends A {@Overridepublic void hi() {// TODO Auto-generated method stubSystem.out.println("你好!");}
}
抽象——模板设计模式
常见需求:
- 有多个类,完成不同的任务job
- 要求统计得到各自完成任务的时间。
public class T2 {public static void main(String[] args) {// TODO Auto-generated method stubAA a = new AA();a.job();}}
class AA {//计算任务:1到100的和public void job() {//得到开始时间long start = System.currentTimeMillis();long sum = 0;for(long i = 1; i<=900000;i++) {sum += i;}//得到结束时间long end = System.currentTimeMillis();System.out.println("运行时间:" + (end - start));}
}
class BB {public void job2() {//得到开始时间long start = System.currentTimeMillis();long sum = 0;for(long i = 1; i<=700000;i++) {sum += i;}//得到结束时间long end = System.currentTimeMillis();System.out.println("运行时间:" + (end - start));}
}===================================================================================
public class T3 {public static void main(String[] args) {// TODO Auto-generated method stubAAA a = new AAA();a.calculateTime();BBB b = new BBB();b.calculateTime();}}
abstract class Template {public abstract void job(); //抽象方法public void calculateTime() {long start = System.currentTimeMillis();job(); //动态机制绑定long end = System.currentTimeMillis();System.out.println("运行时间:" + (end - start));}
}
class AAA extends Template {@Overridepublic void job() { //实现抽象方法long sum = 0;for(long i = 1; i<=900000;i++) {sum += i;}}
}
class BBB extends Template {@Overridepublic void job() { //实现抽象方法long sum = 0;for(long i = 1; i<=800000;i++) {sum += i;}}
}
==================================
运行时间:3
运行时间:2
接口
USB
插槽就是现实中的接口。
快速入门
public class Interface01 {public static void main(String[] args) {// TODO Auto-generated method stubCamera camera = new Camera();Phone phone = new Phone();Computer1 computer1 = new Computer1();computer1.work(phone); //把手机接入计算机computer1.work(camera);}}
interface UsbInterface {//规定接口的相关方法public void start();public void stop();
}//phone类实现UsbInterface
class Phone implements UsbInterface {@Overridepublic void start() {// TODO Auto-generated method stubSystem.out.println("手机开始工作");}@Overridepublic void stop() {// TODO Auto-generated method stubSystem.out.println("手机停止工作");}}
//camera类实现UsbInterface
class Camera implements UsbInterface {@Overridepublic void start() {// TODO Auto-generated method stubSystem.out.println("相机开始工作");}@Overridepublic void stop() {// TODO Auto-generated method stubSystem.out.println("相机停止工作");}
}
class Computer1 {public void work(UsbInterface usbInterface) {//通过接口调用方法usbInterface.start();usbInterface.stop();}
}==================================手机开始工作
手机停止工作
相机开始工作
相机停止工作
基本介绍
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
- 语法:
interface 接口名 {
//属性
//方法
}
class 类名 implements 接口 {
自己属性;
自己方法;
必须实现的接口的抽象方法
}
JDK7
之前接口的所有方法都没有方法体,即都是抽象方法。JDK8
之后接口类可以有静态方法和默认方法,也就是说接口中可以有方法的具体实现。
interface AInterface {//属性public int n1 = 10;//方法public abstract void hello(); //抽象方法//接口中抽象方法可以省略abstractpublic void hello1();//JDK8之后接口类可以有静态方法和默认方法,也就是说接口中可以有方法的具体实现。default public void OK() { //默认实现静态方法System.out.println("OK!");} public static void cry { //静态方法System.out.println("喵喵喵……");}
}
class D implements AInterface {@Overridepublic void hello() {// TODO Auto-generated method stub}@Overridepublic void hello1() {// TODO Auto-generated method stub}}
应用场景
- 现在要制作战斗机,专家只需要把飞机需要的功能、规格定下来即可让别的人具体实现。
使用细节
- 接口不能被实例化。
- 接口中所有的方法是
public
方法,接口中的抽象方法可以不用abstract
修饰。
void aaa(); = abstract void aaa();
- 一个普通类实现接口,就必须将接口中的所有方法都实现。
- 抽象类实现接口,可以不用实现接口的方法。
- 一个类同时可以实现多个接口。【单一继承】
class Pig implements IB,IC {
}
- 接口中的属性只能是
final
,而且是public static final
修饰符。
int a =1 等价于
public static final int a = 1
- 接口中属性的访问形式:接口名.属性名。
- 一个接口不能继承其他的类,但是可以继承多个别的接口。
interface A
extends
B,C { //A、B、C均为接口
}
- 接口的修饰符只能是
public
和默认,这点和类相同。
接口VS继承
- 继承:当子类继承父类,就自动拥有了父类的功能
- 接口——补充机制:如果子类需要扩展功能,可以通过实现接口的方式扩展,可以理解为接口实际上是对
java
单继承机制的补充。 - 接口的价值在于设计规范好的方法,让其他类去实现;而继承的价值在于解决代码的复用性和可维护性。
- 接口比继承更加灵活,继承满足
is-a
的关系,接口满足like-a
的关系。 - 接口在一定程度上实现了代码解耦(即接口规范性+动态绑定)
高内聚低耦合
public class ExtendVSInterface {public static void main(String[] args) {// TODO Auto-generated method stubLittleMonkey wukong = new LittleMonkey("悟空");wukong.climbing();wukong.swimming();}}
class Monkey {private String name;public void climbing() {System.out.println( name + "猴子会爬树……");}public Monkey(String name) {super();this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
//接口——补充机制:如果子类需要扩展功能,可以通过实现接口的方式扩展
//可以理解为接口实际上是对java单继承机制的补充
interface Fish {void swimming();
}
//继承:当子类继承父类,就自动拥有了父类的功能
class LittleMonkey extends Monkey implements Fish{public LittleMonkey(String name) {super(name);// TODO Auto-generated constructor stub}@Overridepublic void swimming() {// TODO Auto-generated method stubSystem.out.println( getName() + "猴子通过学习,向小鱼学会了游泳……");}}
接口多态特性
多态参数
前面的
USB
接口中,形参UsbInterface usbInterface
既可以接收手机对象,也可以接收相机对象,就体现了接口多态。(接口引用可以指向实现了接口的类的对象)
```python
public class Interface01 {public static void main(String[] args) {// TODO Auto-generated method stubCamera camera = new Camera();Phone phone = new Phone();Computer1 computer1 = new Computer1();computer1.work(phone); //把手机接入计算机computer1.work(camera);}}
interface UsbInterface {//规定接口的相关方法public void start();public void stop();
}//phone类实现UsbInterface
class Phone implements UsbInterface {@Overridepublic void start() {// TODO Auto-generated method stubSystem.out.println("手机开始工作");}@Overridepublic void stop() {// TODO Auto-generated method stubSystem.out.println("手机停止工作");}}
//camera类实现UsbInterface
class Camera implements UsbInterface {@Overridepublic void start() {// TODO Auto-generated method stubSystem.out.println("相机开始工作");}@Overridepublic void stop() {// TODO Auto-generated method stubSystem.out.println("相机停止工作");}
}
//UsbInterface usbInterface形参是接口类型
//这个参数实现了接口的类的对象实例
class Computer1 {public void work(UsbInterface usbInterface) {//通过接口调用方法usbInterface.start();usbInterface.stop();}
}
多态参数
多态数组
多态数组
多态传递
public class Interface04 {public static void main(String[] args) {// TODO Auto-generated method stub//接口类型的变量可以指向实现了该接口的类的对象实例IG ig = new Teacher();IH ih = new Teacher(); //Teacher并未实现IH接口//但如果IH继承IG,即Teacher可实现IH接口,体现了多态传递}}
interface IH {void hi();
}
interface IG extends IH{}
class Teacher implements IG {@Overridepublic void hi() {// TODO Auto-generated method stub}
}
练习
public class Interface05 {public static void main(String[] args) {// TODO Auto-generated method stubnew C().px();}}
interface A {int x = 0; //等价于 public static final int x = 0
}
class B {int x = 1; //普通数学
}
class C extends B implements A {public void px() {//System.out.println(x); //报错,不清楚哪个xSystem.out.println(A.x+""+super.x); //访问父类x不可使用B.x}
}
内部类(非常重要、难点)
- 一个类的内部嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类称为外部类。
- 类的五大成员:属性、方法、构造器、代码块、内部类。
- 内部类最大的特点是可以直接访问私有属性,并且体现类与类之间的包含关系。
- 内部类的分类:
- 局部内部类(有类名)-------->定义在外部类的局部位置(比如方法内);
- 匿名内部类(没有类名,重点)-------->定义在外部类的局部位置;
- 成员内部类(没有
static
修饰)------->定义在外部类的成员位置; - 静态内部类(有
static
修饰)------->定义在外部类的成员位置;
局部内部类
1. 局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
2. 可以直接访问外部类的所有成员,包括私有的。
3. 不能添加访问修饰符,因为他的地位就是一个局部变量,局部变量不能使用修饰符。但是可以使用final
修饰。 final
修饰意味着局部内部类不能被继承,不加final
意味着局部内部类可以被继承。
4. 局部内部类的作用域仅仅在定义它的方法或代码块中。
4. 局部内部类可以直接访问外部类的成员。
5. 外部类方法中,可以创建局部内部类对象然后调用方法即可。
6. 本质上还是一个类,即类的五大成员都可以有。
6. 外部其他类不能访问局部内部类(因为局部内部类的地位就是一个局部变量)。
7. 如果外部类和局部内部类的成员重名时,默认.就近原则,如果想访问外部类的成员,则可以使用外部类名.this.成员
去访问。
//演示局部内部类的使用
public class LocalInnerClass {public static void main(String[] args) {// TODO Auto-generated method stubOuter02 outer02 = new Outer02();outer02.m1();}}
class Outer02 { //外部类private int n1 =100;private void m2() {System.out.println("outer02 m2()");} //私有方法public void m1() { //方法//1. 局部内部类是定义在外部类的局部位置,通常在方法//3. 不能添加修饰符,但是可以使用final修饰。//4. 局部内部类的作用域仅仅在定义它的方法或代码块【本质是没有方法名的方法】中。//即该内部类的作用域为m1()方法中。final class Inner02 { //局部内部类,本质仍然是类,即五大成员都可以有。//可以直接访问外部类的所有成员,包括私有的private int n1 = 800;public void f1() { //5. 局部内部类可以直接访问外部类的成员,比如下面的外部类n1和m2()。//7. 如果外部类和局部内部类的成员重名时,默认.就近原则,如果想访问外部类的成员,//则可以使用`外部类名.this.成员`去访问//outer02.this本质上就是外部类的对象即哪个对象调用了m1方法,outer02.this就是哪个对象。System.out.println("内部类的n1=" + n1 + "外部类的n1=" + Outer02.this.n1);m2(); //也可直接访问私有方法}}//6. 外部类方法中,可以创建Inner02对象然后调用方法即可Inner02 inner02 = new Inner02();inner02.f1();}
}
匿名内部类(重点、难点)
匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名。
本质上是类。
内部类。
该类没有名字。
同时还是一个对象。
- 基本语法:
new 类或接口(参数列表){
类体
};
演示基于接口匿名内部类的使用
//演示基于接口匿名内部类的使用
public class Inner01 { //外部其他类public static void main(String[] args) {// TODO Auto-generated method stubOuter01 outer01 = new Outer01();outer01.method();}}
class Outer01 { //外部类private int n1 = 10;public void method() {//1. 基于接口的匿名内部类//2. 需求1:想使用接口A,并创建对象。//3. 传统方法:写一个类,实现该接口并创建对象
// A tiger = new Tiger();
// tiger.cry();//4. 需求2:tiger只是使用一次,后面不再使用。//5. 可以使用匿名内部类来简化开发//6. tiger的编译类型A ,运行类型是匿名内部类,底层分布随机类名Outer01$1,底层是实现关系//7. JDK底层在创建匿名内部类Outer01$1,立马就创建了Outer01$1实例,并且把地址返回给tiger//8. 匿名内部类使用一次就不能再使用了,但对象tiger可以反复调用。A tiger = new A() {@Overridepublic void cry() {System.out.println("老虎叫唤");}};//9. getClass()获取对象的运行类型System.out.println("tiger的运行类型:" + tiger.getClass() );tiger.cry();tiger.cry();tiger.cry();}
}
interface A { //接口public void cry();
}
演示基于类匿名内部类的使用
//演示基于类的匿名内部类的使用
public class Inner02 { //外部其他类public static void main(String[] args) {// TODO Auto-generated method stubOuter02 outer02 = new Outer02();outer02.method();}}
class Outer02 { //外部类private int n1 = 10;public void method() {//基于类的匿名内部类的使用//1. father的编译类型Father//2. father的运行类型Outer02$2//3. 底层会创建匿名内部类 ,底层是继承关系,Outer02$2继承了father类//4. jack形参列表会自动传递给构造器Father father = new Father("jack") { //输出名字是jack @Overridepublic void test() {// TODO Auto-generated method stubSystem.out.println("匿名内部类重写了test方法");} };//getClass()获取对象的运行类型System.out.println("father对象的运行类型:" + father.getClass()); //输出Outer02$2father.test(); //输出匿名内部类重写了test方法//基于抽象类的匿名内部类的使用//抽象类中必须重写eat方法Animal animal = new Animal() {@Overridevoid eat() {// TODO Auto-generated method stubSystem.out.println("吃东西");} };}
}
class Father {public Father(String name) { //构造器System.out.println("名字是" + name);}public void test() { //方法}
}
abstract class Animal {abstract void eat();
}==============================================
名字是jack
father对象的运行类型:class 类变量和类方法.Outer02$1
匿名内部类重写了test方法
使用细节
- 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象。因此,从语法上看,它既有定义类的特征,也有创建对象的特征。
- 可以直接访问外部类的所有成员,包括私有的。
- 不能添加访问修饰符,因为它的低位就是一个局部变量。
- 匿名内部类的作用域仅仅是在它的方法或代码块中。
- 匿名内部类----访问------->外部类成员 【访问方式:直接访问】
- 外部其他类----不能访问------->匿名内部类(因为匿名内部类的地位就是一个局部变量)。
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则。
- 如果想访问外部类的成员,则可以使用
外部类名.this.成员
去访问。
public class Inner03 {public static void main(String[] args) {// TODO Auto-generated method stubOuter03 outer03 = new Outer03();outer03.f1(); //输出第一种调用方法匿名内部类重写了hi方法//如果匿名内部类中未重写hi方法,则输出 Person类中的hi方法}}
class Outer03 { //外部类private int n1 = 99;//6. 外部其他类----不能访问------->匿名内部类//System.out.println(n2);public void f1() {//创建一个基于内部类的匿名//1. 第一种调用方法//3. 不能添加访问修饰符,因为它的低位就是一个局部变量。//Person person = new public Person()Person person = new Person() {private int n2 = 1000;@Overridepublic void hi() {//2. 可以直接访问外部类的所有成员,包括私有的。System.out.println(n1);System.out.println("第一种调用方法匿名内部类重写了hi方法");} };//输出匿名内部类重写了hi方法person.hi(); //运行时存在动态绑定 ,运行类型是Outer03$3//2. 第二种调用方法//也可以直接调用,匿名内部类本身也是返回对象new Person() {private int n2 = 1000;@Overridepublic void hi() {// TODO Auto-generated method stubSystem.out.println("第二种调用方法匿名内部类重写了hi方法");} }.hi();}
}
class Person {public void hi() {System.out.println("Person类中的hi方法");}
}
//抽象类、接口……都可以写
匿名内部类的最佳实践
练习一
public class Inner04 {public static void main(String[] args) {//匿名内部类可以当作实参去传递//f2()方法里传递的ILf2(new IL(){public void show() {System.out.println("这是一副名画");}});//传统方式//写一个类实现IL,将picture传给接口IL------硬编码f2(new Picture());}//静态方法,形参是接口类型public static void f2(IL il) {il.show();}}
interface IL {void show();
}
class Picture implements IL {@Overridepublic void show() {// TODO Auto-generated method stubSystem.out.println("这是一副名画");}}
练习二
- 有一个铃声接口Bell,里面有一个ring方法
- 有一个手机类cellphone,具有闹钟功能alarmblock,参数是Bell类型
- 测试手机的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
- 再传入另一个匿名内部类(对象),打印:小伙伴上课了
public class Inner05 {public static void main(String[] args) {// TODO Auto-generated method stubCellphone cellphone = new Cellphone();cellphone.alarmblock(new Bell() {@Overridepublic void ring() {// TODO Auto-generated method stubSystem.out.println("懒猪起床了");} });cellphone.alarmblock(new Bell() {@Overridepublic void ring() {// TODO Auto-generated method stubSystem.out.println("小伙伴上课了");} });}
}
interface Bell {void ring();
}
class Cellphone {public void alarmblock(Bell bell) {bell.ring();}
}
成员内部类
成员内部类是定义在外部类的成员位置,并且没有
static
修饰
- 可以直接访问外部类的所有成员,包含私有的。
- 可以添加任意访问符,因为它的地位是一个成员。
- 内部类编译后也会产生一个
.class
文件,其命名规则是:外部类名字$内部类名字.class
- 内部类可以使用所有访问限制修饰符,但外部类只能是
public
或缺省访问限制修饰符。
public class MemberInner01 {public static void main(String[] args) {// TODO Auto-generated method stubOuter01 outer01 = new Outer01();outer01.t1();}
}
class Outer01 { //外部类private int n1 = 99;public String name = "张三"; class Inner01 { //成员内部类public void say() {//可以直接访问外部类的所有成员,包含私有的。System.out.println("Outer01的n1=" + n1 + "Outer01的name=" + name);}} public void t1() {Inner01 inner01 = new Inner01();inner01.say();}
}
- 作用域:和外部类成员一样,为整个类体。
- 成员内部类----->访问----->外部类成员(比如:属性)【访问方式:直接访问】。
- 外部类----->访问成员内部类【访问方式:创建对象再访问】。
- 外部其他类------>访问------->成员内部类【三种方法】
public class MemberInner01 { //外部其他类public static void main(String[] args) {// TODO Auto-generated method stubOuter01 outer01 = new Outer01();outer01.t1();//外部其他类使用成员内部类的两种方式//第一种Outer01.Inner01 inner01 = outer01.new Inner01();inner01.say();//第二种;在外部类中编写一个方法可以返回Inner08对象Outer01.Inner01 inner01Instance = outer01.getInner01Instance();inner01Instance.say(); }
}
class Outer01 { //外部类private int n1 = 99;public String name = "张三"; class Inner01 { //成员内部类public void say() {//可以直接访问外部类的所有成员,包含私有的。System.out.println("Outer01的n1=" + n1 + "Outer01的name=" + name);}} public void t1() {Inner01 inner01 = new Inner01();inner01.say();}//该方法返回Inner01的实例public Inner01 getInner01Instance() {return new Inner01();}
}
- 如果外部类和内部类重名时,内部类访问的话,默认就近原则,如果想访问外部类的成员时,可以使用
外部类名.this.成员
去访问。
class A02 { //外部类private int n1 = 10;private static String name = "jack";//定义一个成员内部类class B02 {private int n1 = 20;//可以直接访问外部类的所有成员,包括私有的public void say() {System.out.println("B02 n1 = " + n1 + "A02 n1 = " + A02.this.n1);//B02 n1 = 20//A02 n1 = 10}}
}
静态内部类
是定义在外部类的成员位置,并且有
static
修饰。
- 可以直接访问外部类的所有静态成员,包括私有的,但不能访问非静态成员。
- 可以添加任何访问修饰符,因为它的地位就是一个成员。
- 作用域:同其他的成员,为整个类体。
- 静态内部类------->访问------->外部类(比如:静态属性)【访问方式:直接访问所有静态成员】。
- 外部类------->访问------->静态内部类【访问方式:创建对象再访问】。
- 外部其他类------->访问---------->静态内部类。
- 如果外部类和静态内部类重名时,静态内部类访问默认遵循就近原则,如果想访问外部类成员,则可以使用
外部类名.成员
去访问。
public class StaticInner01 {public static void main(String[] args) { //外部其他类// TODO Auto-generated method stubOuter01 outer01 = new Outer01();outer01.hi();//外部其他类访问静态内部类//方式一://因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)。Outer01.Inner01 inner01 = new Outer01.Inner01();inner01.say();System.out.println("=====================");//方式二://编写一个方法可以返回静态内部类的实例outer01.getInner01();inner01.say();}}
class Outer01 { //外部类private int n1 = 10;private static int n2 = 10;private static String name = "张三";//静态内部类public static class Inner01 {public void say() {//1.可以直接访问外部类的所有静态成员,包括私有的,但不能访问非静态成员。//System.out.println(n1);----->报错//2.静态内部类------->访问------->外部类(比如:静态属性)【访问方式:直接访问所有静态成员】。System.out.println(n2);}}//3. 作用域:同其他的成员,为整个类体。public void hi() {//4.外部类------->访问------->静态内部类【访问方式:创建对象再访问】。Inner01 inner01 = new Inner01();inner01.say();}public Inner01 getInner01() { //静态方法、非静态方法的也可以return new Inner01();}
}
更多推荐
面向对象编程JAVA(高级部分)
发布评论