面向对象编程JAVA(高级部分)

类变量和类方法

类变量

也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。

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 + "加入了游戏!");
	}
}
  1. 语法:访问修饰符 static 数据类型 变量名
  2. 如何访问?
  • 对象名.类变量名【静态变量的访问修饰符的访问权限和范围和普通属性是一样的】
  • 类名.类变量名(推荐使用)
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 {
	
	//类变量
	//类变量的访问必须遵守相关的访问权限,这里是public
	public 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 {
	
	//类变量
	//类变量的访问必须遵守相关的访问权限,这里是public
	private static String name = "郭泳妍";

	public static String getName() {
		return name;
	}

	public static void setName(String name) {
		A.name = name;
	}
}

  1. 当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量。
  2. 类变量与实例变量的区别:类变量是该类的所有对象共享的,实例变量是每个对象独享的。
  3. 加上static称为类变量或者静态变量,否则称为实例变量/普通变量/非静态变量。
  4. 类变量在加载时就已经被初始化,意思是即使没有创建对象,只要类加载了就可以使用类变量。
  5. 类变量的生命周期是随着类的加载开始的,随着类的消亡而销毁。

类方法/静态方法

  1. 语法:访问修饰符 static 数据返回类型 方法名() { }
  2. 如何调用?
  • 类名.类方法.
  • 对象名.类方法名【前提是满足访问修饰符的访问权限和范围】
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);
	}
}
  1. 经典使用场景:当方法中不涉及到任何和对象有关的成员,即不创建实例也可以调用某个方法(也就是说当成工具使用),则可以将方法设计成静态方法提升开发效率。
System.out.println("9开方的结果是:" + Math.sqrt(9));
  1. 开发自己的工具时,可以将方法做成静态的,方便进行调用。
  2. 类方法和普通方法都是随着类的加载而加载的,将结构信息存储在方法区中。
  3. 类方法中无this这个参数,而普通方法中隐含着this这个参数。
  4. 类方法可以通过类名和对象名来调用。
  5. 普通方法和对象有关,需要通过对象名来调用,比如对象名.方法名(参数),不能通过类名调用。
  6. 类方法中不允许使用和对象有关的关键字,如this、super,而普通方法可以。
  7. 类方法中只能访问静态变量或者静态方法。
  8. 普通成员既可以访问普通变量,也可以访问静态变量。
  9. 静态方法只能访问静态的成员;非静态的方法,可以访问静态成员和非静态成员。
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方法

  1. 语法:
public static void main(String [] args)
  1. Java虚拟机需要调用类的main方法,所以该方法的访问权限必须要是public,否则运行不了。
  2. java虚拟机在执行main方法时不必创建对象,作为程序入口,所以该方法必须是static
  3. 该方法接收Sting类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。
    在这里插入图片描述
  4. main()方法中,可以直接调用main方法所在类的静态方法和静态属性。
  5. 但是,不能直接访问该类中的非静态成员,必须创建一个实例对象后才可以访问。
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动态传值

不同的编译器和不同的版本在网上查询教程。

代码块/初始化块

语法+概念

  1. 属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过{ }包围起来。
  2. 但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是记载类时,或者创建对象时隐式调用。
  3. 基本语法:

[修饰符] {
代码
};

注意

  • 修饰符可选,要写的话只能写static
  • 代码块分两类,使用static修饰的叫静态代码块,没有static修饰的叫做普通代码块/非静态代码块。
  • 逻辑语句可以分为任意逻辑语句。
  • ;可以写上,也可以省略。

代码块的好处:

  1. 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作。
  2. 应用场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码重用性。
  • 三个构造器中都要有相同的输出语句,这时可以把相同的语句放入一个代码块中即可。
  • 这样当我们不管调用哪个构造器,创建对象都会先调用代码块内容。
  • 代码块调用的顺序优先于构造器。
public class CodeBlock01 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Movie 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的静态代码块被执行!");
	}
}

创建一个对象时,在一个类调用顺序(重点、难点)

  1. 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化块调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们的定义顺序调用)。
  2. 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和普通属性初始化则按他们的定义顺序调用)
  3. 调用构造方法
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 stub
		
		
		AAA  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()构造器被调用

创建一个子类时(继承关系),他们的静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序(难点、面试题)

  • 父类的静态代码块和静态属性(优先级相同,按定义顺序调用)。
  • 子类的静态代码块和静态属性(优先级相同,按定义顺序调用)。
  • 父类的普通代码块和普通属性初始化(优先级相同,按定义顺序调用)。
  • 父类的构造方法。
  • 子类的普通代码块和普通属性初始化(优先级相同,按定义顺序调用)。
  • 子类的构造方法。

练习

在这里插入图片描述

在这里插入图片描述

静态代码块可以只能调用静态成员,普通代码块可以调用任意成员。

单例设计模式

设计模式的概念

  1. 静态方法和属性的经典使用。
  2. 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。

单例模式

就是采用一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

单例模式饿汉类

类被加载后即全部对象都创建【创建后未被利用,可能造成资源浪费】

  • 构造器私有化(防止直接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返回,需将其修饰为static
	private 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 stub
		System.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;  //默认是null
	private Cat(String name) {
		System.out.println("构造器被调用");
		this.name = name;
	}
	//懒汉式中,只有当用户使用getInstance() 时才会返回cat对象,
	//后面再次调用时返回上次创建的cat对象
	//从而保证了单例
	public static Cat getInstance() {
		if(cat == null) {
			cat = new Cat("小可爱");
		}
		return cat;
	}
	@Override
	public String toString() {
		return "Cat [name=" + name + "]";
	}
	
}


===================================================================================

100
构造器被调用
Cat [name=小可爱]
Cat [name=小可爱]
true

饿汉式与懒汉式区别

  • 最主要的区别在于创建对象的时机不同:饿汉式是类加载时就创建了对象实例,而懒汉式是在使用时才创建。
  • 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。
  • 饿汉式存在浪费资源的可能。

final关键字

final可以修饰类、属性、方法、局部变量。

应用场景

  1. 当不希望类被继承时,可以用final修饰。
  2. 但不希望父类的某个方法被子类覆盖或者重写时,可以用final关键字修饰。
public final void hi(){}
  1. 当不希望类的某个属性的值被修改但是仍然可以调用。
public final double TAX = 0.09;
  1. 当不希望某个局部变量被修改。
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 {

	@Override
	public void hi() {
		// TODO Auto-generated method stub
		super.hi();
		System.out.println("重写了C类的hi方法");
	}
	
}

使用细节

  1. final修饰的属性又叫常量,一般字母全部是大写,且字母用下划线隔开。如TAX_NUM
  2. 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;         //在代码块中
	}
}
  1. 如果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;          //在静态代码块中
	}
	
}
  1. final类不能继承,但是可以实例化对象。
CC cc = new CC()
  1. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承,可以在主类中创建对象时调用。
  2. 一般来说,如果一个已经是final类,就没有必要将方法修饰成final方法。
  3. final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理。
public class Final02 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(DD.num1);    //100
		System.out.println(DD.num2);    //DD静态代码块被执行  200
	} 

}
class DD {
	public final static int num1 = 100;
	public static int num2 = 200;
	static {
		System.out.println("DD静态代码块被执行");
	}
}	
  1. 包装类(Interger、Double、Float、Boolean等都是final),String类也是final类,这些都布不能被继承。

抽象类

概念

  1. 当父类的某个方法需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。
  2. 当一个类中存在抽象方法时,需要将该类声明为abstract类。
  3. 一般来说抽象类会被继承,由其子类实现抽象方法。

介绍

  1. abstract修饰的类就是抽象类。

访问修饰符 abstract 类名 {

}

  1. abstract修饰的方法就是抽象方法。

访问修饰符 abstract 返回类型 方法名 (参数列表); //没有方法体

  1. 抽象类的价值更多在于设计,是设计师设计好后,让子类继承并实现抽象类。
  2. 抽象类在面试中常见。

使用细节

  1. 抽象类不能实例化。
  2. 抽象类不一定包含抽象方法,还可以有实现方法。
  3. 一旦类包含了abstract方法,则这个类必须声明为abstract
  4. abstract 只能修饰类和方法,不能修饰属性和其他的。
  5. 抽象类还是类,可以有任意成员,比如:非抽象方法、构造器、静态属性等等。
  6. 抽象方法不能有主体,即不能实现。【没有方法体】
  7. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非他自己也声明为abstract类。【所谓实现方法,就是要有方法体】。
  8. 抽象方法不能使用private、final、static修饰,因为这些关键字都是和重写相违背的。
abstract class A {
	public abstract void hi();	
}
//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非他自己也声明为abstract类。
abstract class B extends A {
	
}
class C extends A {

	@Override
	public void hi() {
		// TODO Auto-generated method stub
		System.out.println("你好!");
	}	
}

抽象——模板设计模式

常见需求:

  1. 有多个类,完成不同的任务job
  2. 要求统计得到各自完成任务的时间。
public class T2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		AA a = new AA();
		a.job();
	}

}
class AA {
	//计算任务:1100的和
	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 stub
		AAA 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 {
	@Override
	public void job() {   //实现抽象方法
		long sum = 0;
		for(long i = 1; i<=900000;i++) {
			sum += i;
		}
	}
}
class BBB extends Template {
	@Override
	public 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 stub
		Camera 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 {

	@Override
	public void start() {
		// TODO Auto-generated method stub
		System.out.println("手机开始工作");
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		System.out.println("手机停止工作");
	}
	
}
//camera类实现UsbInterface
class Camera implements UsbInterface {
	@Override
	public void start() {
		// TODO Auto-generated method stub
		System.out.println("相机开始工作");
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		System.out.println("相机停止工作");
	}
}
class Computer1 {
	public void work(UsbInterface usbInterface) {
		//通过接口调用方法
		usbInterface.start();
		usbInterface.stop();
	}
}

==================================

手机开始工作
手机停止工作
相机开始工作
相机停止工作

基本介绍

  1. 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
  2. 语法:

interface 接口名 {
//属性
//方法
}
class 类名 implements 接口 {
自己属性;
自己方法;
必须实现的接口的抽象方法
}

  1. JDK7之前接口的所有方法都没有方法体,即都是抽象方法。
  2. JDK8之后接口类可以有静态方法和默认方法,也就是说接口中可以有方法的具体实现。
interface AInterface {
	//属性
	public int n1 = 10;
	//方法
	public abstract void hello();    //抽象方法
	//接口中抽象方法可以省略abstract
	public void hello1();
	//JDK8之后接口类可以有静态方法和默认方法,也就是说接口中可以有方法的具体实现。
	default public void OK() {       //默认实现静态方法
		System.out.println("OK!");
	} 
	public static void cry {       //静态方法
		System.out.println("喵喵喵……");
	}
}
class D implements AInterface {
	@Override
	public void hello() {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void hello1() {
		// TODO Auto-generated method stub
		
	}
	
}

应用场景

  1. 现在要制作战斗机,专家只需要把飞机需要的功能、规格定下来即可让别的人具体实现。

使用细节

  1. 接口不能被实例化。
  2. 接口中所有的方法是public方法,接口中的抽象方法可以不用abstract修饰。

void aaa(); = abstract void aaa();

  1. 一个普通类实现接口,就必须将接口中的所有方法都实现。
  2. 抽象类实现接口,可以不用实现接口的方法。
  3. 一个类同时可以实现多个接口。【单一继承】

class Pig implements IB,IC {
}

  1. 接口中的属性只能是final,而且是public static final修饰符。

int a =1 等价于
public static final int a = 1

  1. 接口中属性的访问形式:接口名.属性名。
  2. 一个接口不能继承其他的类,但是可以继承多个别的接口。

interface A extends B,C { //A、B、C均为接口
}

  1. 接口的修饰符只能是public和默认,这点和类相同。

接口VS继承

  1. 继承:当子类继承父类,就自动拥有了父类的功能
  2. 接口——补充机制:如果子类需要扩展功能,可以通过实现接口的方式扩展,可以理解为接口实际上是对java单继承机制的补充。
  3. 接口的价值在于设计规范好的方法,让其他类去实现;而继承的价值在于解决代码的复用性和可维护性。
  4. 接口比继承更加灵活,继承满足is-a的关系,接口满足like-a的关系。
  5. 接口在一定程度上实现了代码解耦(即接口规范性+动态绑定)
    高内聚低耦合
public class ExtendVSInterface {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		LittleMonkey 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
	}

	@Override
	public void swimming() {
		// TODO Auto-generated method stub
		System.out.println( getName() + "猴子通过学习,向小鱼学会了游泳……");
	}
	
}

接口多态特性

多态参数

前面的USB接口中,形参UsbInterface usbInterface既可以接收手机对象,也可以接收相机对象,就体现了接口多态。(接口引用可以指向实现了接口的类的对象)


```python
public class Interface01 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Camera 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 {

	@Override
	public void start() {
		// TODO Auto-generated method stub
		System.out.println("手机开始工作");
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		System.out.println("手机停止工作");
	}
	
}
//camera类实现UsbInterface
class Camera implements UsbInterface {
	@Override
	public void start() {
		// TODO Auto-generated method stub
		System.out.println("相机开始工作");
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		System.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 {

	@Override
	public void hi() {
		// TODO Auto-generated method stub
		
	}	
}

练习

public class Interface05 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new 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);   //报错,不清楚哪个x
		System.out.println(A.x+""+super.x);  //访问父类x不可使用B.x
	}
}

内部类(非常重要、难点)

  1. 一个类的内部嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类称为外部类。在这里插入图片描述
  2. 类的五大成员:属性、方法、构造器、代码块、内部类。
  3. 内部类最大的特点是可以直接访问私有属性,并且体现类与类之间的包含关系。
  4. 内部类的分类:
    • 局部内部类(有类名)-------->定义在外部类的局部位置(比如方法内);
    • 匿名内部类(没有类名,重点)-------->定义在外部类的局部位置;
    • 成员内部类(没有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 stub
		Outer02 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();
	}
}

匿名内部类(重点、难点)

匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名。
本质上是类。
内部类。
该类没有名字。
同时还是一个对象。

  1. 基本语法:

new 类或接口(参数列表){
类体
};

演示基于接口匿名内部类的使用

//演示基于接口匿名内部类的使用
public class Inner01 {    //外部其他类

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Outer01 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() {
			@Override
			public 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 stub
		Outer02 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			
			@Override
			public void test() {
				// TODO Auto-generated method stub
				System.out.println("匿名内部类重写了test方法");
			}			
		};
		//getClass()获取对象的运行类型
		System.out.println("father对象的运行类型:" + father.getClass());  //输出Outer02$2
		father.test();        //输出匿名内部类重写了test方法
		
		
		//基于抽象类的匿名内部类的使用
		//抽象类中必须重写eat方法
		Animal animal = new Animal() {
			
			@Override
			void eat() {
				// TODO Auto-generated method stub
				System.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方法

使用细节

  1. 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象。因此,从语法上看,它既有定义类的特征,也有创建对象的特征。
  2. 可以直接访问外部类的所有成员,包括私有的。
  3. 不能添加访问修饰符,因为它的低位就是一个局部变量。
  4. 匿名内部类的作用域仅仅是在它的方法或代码块中。
  5. 匿名内部类----访问------->外部类成员 【访问方式:直接访问】
  6. 外部其他类----不能访问------->匿名内部类(因为匿名内部类的地位就是一个局部变量)。
  7. 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则。
  8. 如果想访问外部类的成员,则可以使用外部类名.this.成员去访问。
public class Inner03 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Outer03 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;
			@Override
			public void hi() {
				//2. 可以直接访问外部类的所有成员,包括私有的。
				System.out.println(n1);
				System.out.println("第一种调用方法匿名内部类重写了hi方法");
			}			
		};
		//输出匿名内部类重写了hi方法
		person.hi();  //运行时存在动态绑定 ,运行类型是Outer03$3
		
		
		//2. 第二种调用方法
		//也可以直接调用,匿名内部类本身也是返回对象
		new Person() {
			private int n2 = 1000;
			@Override
			public void hi() {
				// TODO Auto-generated method stub
				System.out.println("第二种调用方法匿名内部类重写了hi方法");
			}		
		}.hi();
	}
}
class Person {
	public void hi() {
		System.out.println("Person类中的hi方法");
	}
}
//抽象类、接口……都可以写

匿名内部类的最佳实践

练习一
public class Inner04 {

	public static void main(String[] args) {
		
		//匿名内部类可以当作实参去传递
		//f2()方法里传递的IL
		f2(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 {

	@Override
	public void show() {
		// TODO Auto-generated method stub
		System.out.println("这是一副名画");
	}
	
}
练习二
  1. 有一个铃声接口Bell,里面有一个ring方法
  2. 有一个手机类cellphone,具有闹钟功能alarmblock,参数是Bell类型
  3. 测试手机的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
  4. 再传入另一个匿名内部类(对象),打印:小伙伴上课了
public class Inner05 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Cellphone cellphone = new Cellphone();
		cellphone.alarmblock(new Bell() {

			@Override
			public void ring() {
				// TODO Auto-generated method stub
				System.out.println("懒猪起床了");
			}			
		});
		
		cellphone.alarmblock(new Bell() {

			@Override
			public void ring() {
				// TODO Auto-generated method stub
				System.out.println("小伙伴上课了");
			}			
		});
	}
}
interface Bell {
	void ring();
}
class Cellphone {
	public void alarmblock(Bell bell) {
		bell.ring();
	}
}

成员内部类

成员内部类是定义在外部类的成员位置,并且没有static修饰

  1. 可以直接访问外部类的所有成员,包含私有的。
  2. 可以添加任意访问符,因为它的地位是一个成员。
  3. 内部类编译后也会产生一个.class文件,其命名规则是:外部类名字$内部类名字.class
  4. 内部类可以使用所有访问限制修饰符,但外部类只能是public或缺省访问限制修饰符。
public class MemberInner01 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Outer01 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();
	}
}
  1. 作用域:和外部类成员一样,为整个类体。
  2. 成员内部类----->访问----->外部类成员(比如:属性)【访问方式:直接访问】。
  3. 外部类----->访问成员内部类【访问方式:创建对象再访问】。
  4. 外部其他类------>访问------->成员内部类【三种方法】
public class MemberInner01 {  //外部其他类

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Outer01 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();
	}
}

  1. 如果外部类和内部类重名时,内部类访问的话,默认就近原则,如果想访问外部类的成员时,可以使用外部类名.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修饰。

  1. 可以直接访问外部类的所有静态成员,包括私有的,但不能访问非静态成员。
  2. 可以添加任何访问修饰符,因为它的地位就是一个成员。
  3. 作用域:同其他的成员,为整个类体。
  4. 静态内部类------->访问------->外部类(比如:静态属性)【访问方式:直接访问所有静态成员】。
  5. 外部类------->访问------->静态内部类【访问方式:创建对象再访问】。
  6. 外部其他类------->访问---------->静态内部类。
  7. 如果外部类和静态内部类重名时,静态内部类访问默认遵循就近原则,如果想访问外部类成员,则可以使用外部类名.成员去访问。
public class StaticInner01 {

	public static void main(String[] args) {    //外部其他类
		// TODO Auto-generated method stub
			Outer01 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();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

poggioxay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值