【Java课程心得】高级类的特征二 终---初始化块、抽象类、接口、内部类||关键字static、final,包括单例设计模式、模板方法设计模式

1.static关键字

   --------静态的
         当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。

在这里插入图片描述

问题:有没有办法能只写一次国籍,让所有的对象都使用者一个相同的国籍

		public class Students{
    
    
		String country;
		String name;
		int age;
		}
		

		Chinese c = new Chinese();
		c.country = "中国";
		c.name = "xx";
		c.age = 11;
		Chinese c1 = new Chinese();
		c1.country = "中国";''
		c1.name = "xx";
		c1.age = 11;
		Chinese c2 = new Chinese();
		c2.country = "中国";
		c2.name = "xx";
		c2.age = 11;
				
		//有没有办法能只写一次国籍,让所有的对象都使用者一个相同的国籍

解决方法:

public class Students{
    
    
		static String country;
		String name;
		int age;
		}

测试代码
		Chinese.country = "中国";
		Chinese c = new Chinese();
		c.name = "xx";
		c.age = 11;
		Chinese c1 = new Chinese();
		c1.name = "xx";
		c1.age = 11;
		Chinese c2 = new Chinese();
		c2.name = "xx";
		c2.age = 11;
		
		System.out.println(c.country);
		System.out.println(c1.country);
		System.out.println(c2.country);
		System.out.println(Chinese.country);

如果想让一个类的所有实例共享数据,就用类变量!

①static String country;
类变量不用实例化,直接类名.属性名就可以使用,是类的一部分,被所有这个类的实例化对象所共享,也可以叫做静态变量

②String name;
    int age;

实例对象,只有实例化之后才能使用,属于实例化对象的一部分,不能共用

类变量、类方法

①类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
同样,有些方法不想因为对象的不同而频繁通过new对象方式去调用方法,方法就成static

②如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用

注意:类方法会经常使用,经常做成工具类

代码①:

	public class Chinese {
    
    
	
	static String country;
		   String name;
		   int age;

	public static void test(){
    
    
		System.out.println("这是一个静态方法");
	}


测试代码
	System.out.println(Chinese.country);
	Chinese.test();

///中国
///输出这是一个静态方法
}	

工具类例子:


	String s = null;
		if(s != null && !s.equals("")){
    
    
			//在未来的开发中,可能会多次使用这一的判断,那么在大量次数的基础上看,就发现代码的重复就很多了
			//所以我们把这一的代码给抽取到工具类做成一个方法
		}


/工具类设计:

public class Utils {
    
    
	//判断字符串是不是一个空字符串
	public static boolean isEmpty(String s){
    
    
		boolean flag = false;
		if(s != null && !s.equals("")){
    
    
			flag = true;
		}
		return flag;
	}
}


///测试:
	System.out.println(Utils.isEmpty(s));
	

用法:

使用范围:
    在Java类中,可用static修饰属性、方法、代码块、内部类

被修饰后的成员具备以下特点:

  • 随着类的加载而加载-----类加载之后,静态的方法或者属性就能用,类名.
  • 优先于对象存在-----不用new就能用
  • 修饰的成员,被所有对象所共享
  • 访问权限允许时,可不创建对象,直接被类调用

注意:因为不需要实例就可以访问static方法,因此static方法内部不能有this,也不能有super。
           重载的方法需要同时为static的或者非static的。

修饰的成员,被所有对象所共享----记录new了多少个Chinese对象

public class Chinese {
    
    
	public Chinese(){
    
    //构造一个构造方法,使每次调用都可以count+1
		Chinese.count += 1;
	}
	
	static String country;
	public static int count;//计数
	String name;
	int age;
	
	public static void test(){
    
    
		System.out.println("这是一个静态方法");
	}
	
	public static void showCount(){
    
    
		System.out.println("总共new了 " + Chinese.count + " 个对象");
	}
}


/测试代码:

		Chinese c1 = new Chinese();
		System.out.println(Chinese.count);
		Chinese c2 = new Chinese();
		System.out.println(Chinese.count);
		Chinese c3 = new Chinese();
		System.out.println(Chinese.count);
		Chinese c4 = new Chinese();
		System.out.println(Chinese.count);
		Chinese c5 = new Chinese();
		System.out.println(Chinese.count);
		Chinese c6 = new Chinese();
		System.out.println(Chinese.count);
		
		Chinese.showCount();
		
		//由此可以看出Chinese.count这个类属性被所有的实例化对象共享了

输出:
1
2
3
4
5
6
共调用6

构造一个构造方法,使每次调用都可以count+1
public static int count;静态属性整明count是被共用了


例子②:

class Person {
    
    
           private int id;
           public static int total = 0;
           public Person() {
    
    
 	         total++;
 	         id = total;
           }
           public static void main(String args[]){
    
    
         	Person Tom=new Person();
	Tom.id=0;
	total=100; // 不用创建对象就可以访问静态成员
         }
 }

public class OtherClass {
    
    
            public static void main(String args[]) {
    
    
 	         Person.total = 100;  // 不用创建对象就可以访问静态成员
                           //访问方式:类名.类属性,类名.类方法
	         System.out.println(Person.total);
	         Person c = new Person(); 
	         System.out.println(c.total);	//输出101
            }
      }

类变量,这种可以被所有的实例化对象共享的属性,使用起来要慎重,因为只有一改,所有类都能得到变化


2.单例(Singleton)设计模式

-----设计模式就是在我们实际编程过程中,逐渐总结出的一些解决问题的套路

-----单例(Singleton)只有一个实例(实例化对象)

      设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。
      所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。

使用环境:在整个软件系统运行过程中,这个类只被实例化一次,以后不论在哪都只调用这一个实例

例如实例化对象的创建要消耗大量的时间和资源

  • 假设构造中药执行1000行代码,要占用很大的资源,耗时很长(10秒)
    每一次new Single对象需要10秒,运行1000行代码
    像这种情况,就适合使用单例模式,只new一次对象,以后一直就使用这个对象

使用单例模式解决什么问题?一般都是new 对象太费劲或者是频繁的new新对象没意义


饿汉式

/**
 * 饿汉式的单例模式
 * @author lby
 *
 */
public class Single {
    
    
	//私有的构造,构造方法私有化,调用这个类的人就不能直接使用new来创建对象
	private Single(){
    
    
		
	}
	
	//私有的Single类型的类变量,注意是静态
	private static Single single = new Single();
	
	public static Single getInstance(){
    
    //公有静态,可以用对象名.去调用
		return single;
	}
}

测试代码
//		Single s = new Single(); 编译报错,不能new
		Single s = Single.getInstance();
		Single s1 = Single.getInstance();
		Single s2 = Single.getInstance();
		Single s3 = Single.getInstance();
		Single s4 = Single.getInstance();


代码分析:无论执行多少次代码,都是调用的Single.getInstance( )方法,而Single.getInstance( )对象return的是single类变量,而私有的类变量整个代码中只有一份,那么就是说这个new Single只用了一次new。


懒汉式

最开始,对象是null,直到有第一个人调用我,才new 一个对象,之后所有调用我的都用这个对象

/**
 * 懒汉式的单例模式
 * @author lby
 *
 */
public class Single1 {
    
    
	//先私有化构造方法,让外边不能直接new对象
	private Single1(){
    
    
		
	}
	
	private static Single1 s1 = null;
	
	public static Single1 getInstance(){
    
    
		if(s1 == null){
    
    
			s1 = new Single1();
		}
		
		return s1;
	}
}


代码示例

		Single1 s = Single1.getInstance();//第一次将null变为new Single
		Single1 s1 = Single1.getInstance();//后面都是取得同一个single对象
		Single1 s2 = Single1.getInstance();
		Single1 s3 = Single1.getInstance();
		Single1 s4 = Single1.getInstance();

代码分析:第一个人进来的时候已经把s1调整成new Single1,当第二个人进来的时候s1就不是null,然后直接返回s1。


总结

单例模式,软件的运行有且仅有一个实例化对象,(只会new一次)

懒汉式和饿汉武的区别,就是什么时候new这个对象

懒汉式,是在第一次有人调用getlnstance方法时来new对象,以后再有人调用 getlnstance方法直接就返回之前第一次new好的对象。

饿汉式,是在类加载之后,还没有人调用的时候,就先new好一个对象,以后无论谁调用 getlnstance方法,都是返回之前new好的那个对象

java本身的一个单例模式
在这里插入图片描述


3.理解main方法的语法

在这里插入图片描述
最常见到的main方法

      由于java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。

在这里插入图片描述


4.类的成员之四:初始化块

在new Person( );执行的时候
1、类的属性的默认初始化和显示初始化
2、执行代码块的代码(如有多个代码块则按顺序执行)
3、执行构造器的代码

在这里插入图片描述

public class Person {
    
    
	String name;
	
	public Person(){
    
    
		this.name = "张三";
		System.out.println("执行的是构造方法");
	}
	
	//非静态的代码块
	{
    
    
		this.name = "张三";
		System.out.println("执行的是非静态代码块1");
	}
	
	{
    
    
		System.out.println("执行的是非静态代码块2");
	}
	
	{
    
    
		System.out.println("执行的是非静态代码块3");
	}
}	


运行测试
public static void main(String[] args) {
    
    
		new Person();
		}

结果
执行的是非静态代码块1
执行的是非静态代码块2
执行的是非静态代码块3
执行的是构造方法

静态代码块

  • 一个类中初始化块若有修饰符,则只能被static修饰,称为静态代码块(static block ),当类被载入时,类属性的声明和静态代码块先后顺序被执行,且只被执行一次。
public class Person {
    
    
	String name;
	static int age = 1;
	static TestPerson tp = new TestPerson();
	
	public Person(){
    
    
		this.name = "张三";
		System.out.println("执行的是构造方法");
	}
	
	//非静态的代码块
	{
    
    
		this.name = "张三";
		System.out.println("执行的是非静态代码块1");
	}
	
	
	//静态代码块
	static{
    
    
		//这里只能使用静态static修饰的属性和方法
		age = 1;//age是静态的
		showAge();//showage是静态的
		System.out.println("===执行的是静态代码块===");
//		tp.name = "";
//		tp.age = 1;
	}
	
	public static void showAge(){
    
    
		System.out.println(age);
	}
	
	public void test(){
    
    
		System.out.println("Person的test方法");
	}
}

总结

非静态代码块:没有static修饰的代码块
      1.可以有输出语句。
      2.可以对类的属性声明进行初始化操作。
      3.可以调用静态和非静态的变量或方法。
      4.若有多个非静态的代码块,那么按照从上到下的顺序依
次执行。
      5.每次创建对象的时候,都会执行一次。且先于构造器执行

静态代码块:用static 修饰的代码块
      1.可以有输出语句。
      2.可以对类的属性声明进行初始化操作。
      3.不可以对非静态的属性初始化。即:不可以调用非静态的属
性和方法。
      4.若有多个静态的代码块,那么按照从上到下的顺序依次执行。
      5.静态代码块的执行要先于非静态代码块。
      6.静态代码块只执行一次

在实际开发中,static静态代码块用在初始化类的静态属性(static类型属性)

匿名内部类

public class Test {
    
    
	public static void main(String[] args) {
    
    
		new Person();
		new Person();
		
		//匿名内部类
		Person p = new Person(){
    
    //这就一个Person的匿名子类
			//问题?现在想把name改成李四,但是不想动Person的代码
			{
    
    //在匿名内部类中,用代码块代替构造方法,为类属性初始化
				super.name = "李四";
			}
			@Override
			public void test() {
    
    
				System.out.println("====");
			}
		};
		
		System.out.println(p.name);
		p.test();
}

5.关键字final

在Java中声明类、属性和方法时,可使用关键字final来修饰,表示“最终”。

      ①final标记的类不能被继承。提高安全性,提高程序的可读性。
         String类、System类、StringBuffer类

      ②final标记的方法不能被子类重写。
            Object类中的getClass()。

      ③final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
            final标记的成员变量必须在声明的同时或在每个构造方法中或代码块中显式赋值,然后才能使用。
            final double PI=3.14;

1.final修饰类

final class A{
    
    
}
class B extends A{
    
         //错误,不能被继承。
}
Final修饰的类不能被继承

2.final修饰方法

class A{
    
    
       public final void print(){
    
    
                System.out.println(“A”);
       }
}
class B extends A{
    
         
        public void print(){
    
       //错误,不能被重写。
                  System.out.println(“众软”);
         }
}

3.final修饰变量——常量

-------static final:全局常量


final String NAME = "";//final修饰的变量是常量,常量必须显示赋值
	final static String NAME_1 = "";//final static一起修饰变量,就是全局常量
	//常量定义名称约定使用大写,如果多个单词组成名称,用_连接
	//常量只能赋值一次,不能再改变


class  A{
    
    
        private final String INFO = “众软”;  //声明常量
        public void print(){
    
    
                  //INFO = “众软”;
         }
}


final举例

final代表最终,可以修饰变量、方法、类

修饰变量,即为常量,只能赋值一次,不能改变
修饰方法,子类不能重写
修饰类,类不能被继承

public final class Test{
    
    
		public static int totalNumber = 5 ;
		public final int ID;
		public Test(){
    
    
			ID = ++totalNumber;  //可在构造方法中给final变量赋值
		}
   		public static void main(String[] args) {
    
    
			Test t = new Test();
			System.out.println(t.ID);		
			final int I = 10;
			final int J;
			J = 20;
			J = 30;  //非法
    		}
    }

6.抽象类(abstract class)

      随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

在这里插入图片描述

分析在Animal中的move方法里,无法将具体的动物移动方法写在动物类里,所有动物类和其方法都是抽象化处理,就不写具体的实现。

      ①用abstract关键字来修饰一个类时,这个类叫做抽象类;-----只要类中有一个抽象方法,那么这个类就必须是抽象类

      ②用abstract来修饰一个方法时,该方法叫做抽象方法。
         抽象方法:只有方法的声明,没有方法的实现。以分号结束:abstract int abstractMethod( int a );

      ③含有抽象方法的类必须被声明为抽象类。

      ④抽象类不能被实例化。抽象类是用来作为父类被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。

      ⑤不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。

例子①:

public abstract class Animal {
    
    
	public abstract void test();//只要类中有一个抽象方法,类就必须是一个抽象类
	
	public abstract void move();
	
}

class Dog extends Animal{
    
    
	@Override
	public void test() {
    
    
		
	}

	@Override
	public void move() {
    
    
		System.out.println("狗的移动方式是跑");
	}
}

class Fish extends Animal{
    
    
	@Override
	public void test() {
    
    
		
	}

	@Override
	public void move() {
    
    
		System.out.println("鱼的移动方式是游");
	}
}

abstract class Bird extends Animal{
    
    //抽象类可以继承抽象类
	@Override
	public void move() {
    
    
		
	}
	
	public abstract void test();//只要类中有一个抽象方法,类就必须是一个抽象类
}

例子②:

      在航运公司系统中,Vehicle类需要定义两个方法分别计算运输工具的燃料效率和行驶距离

      问题:卡车(Truck)和驳船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同。Vehicle类不能提供计算方法,但子类可以。

/*

解决方案
	Java允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由
	子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。

Vehicle是一个抽象类,有两个抽象方法。
*/

public abstract class Vehicle{
    
    
	public abstract double calcFuelEfficiency();	//计算燃料效率的抽象方法
	public abstract double calcTripDistance();	//计算行驶距离的抽象方法
}
public class Truck extends Vehicle{
    
    
	public double calcFuelEfficiency( )   {
    
     //写出计算卡车的燃料效率的具体方法   }
	public double calcTripDistance( )    {
    
      //写出计算卡车行驶距离的具体方法   }
}

public class RiverBarge extends Vehicle{
    
    
	 public double calcFuelEfficiency( ) {
    
     //写出计算驳船的燃料效率的具体方法  }
	 public double calcTripDistance( )  {
    
      //写出计算驳船行驶距离的具体方法}
}

注意:抽象类不能实例化 new Vihicle()是非法的

一些小问题与示例

问题1:为什么抽象类不可以使用final关键字声明?

  • final是最终,它修饰的类最终的类,不能被继承
  • 抽象类,如果想要使用,必须继承抽象类,实现那些抽象的方法

问题2:一个抽象类中可以定义构造器吗?

  • 抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已。
    抽象类不能实例化 new Vihicle()是非法的

示例要求:编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。提供必要的构造器和抽象方法:work()。对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。

public abstract class Employee {
    
    
	public Employee(){
    
    
		
	}
	
	int id;
	String name;
	double salary;
	
	public abstract void work();
}

class CommonEmployee extends Employee{
    
    

	public void setCommonEmployeeInfo(int id,String name,double salary){
    
    
		super.id = id;
		super.name = name;
		super.salary = salary;
	}
	
	public void getCommonEmployeeInfo(){
    
    
		System.out.println(super.id);
		System.out.println(super.name);
		System.out.println(super.salary);
	}
	
	@Override
	public void work() {
    
    
		System.out.println("这是一个普通员工");
	}
	
}

class Manager extends Employee{
    
    
	double bonus;
	
	public void setManagerInfo(int id,String name,double salary,double bonus){
    
    
		super.id = id;
		super.name = name;
		super.salary = salary;
		
		this.bonus = bonus;
	}
	
	public void getManagerInfo(){
    
    
		System.out.println(super.id);
		System.out.println(super.name);
		System.out.println(super.salary);
		System.out.println(this.bonus);
	}
	
	@Override
	public void work() {
    
    
		System.out.println("这是领导");
	}
	
}

///测试代码
CommonEmployee ce = new CommonEmployee();
ce.work();
ce.setCommonEmployeeInfo(123, "张三", 6354.31);
ce.getCommonEmployeeInfo();

※模板方法设计模式(TemplateMethod)

-----抽象类就像一个大纲,里面的抽象方法就是每个章节的标题
子类,去根据这些标题表每个章节写出来

         抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

解决的问题:
         ①当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
         ②编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。

abstract class Template{
    
    
	public final void getTime(){
    
    
		long start = System.currentTimeMillis();//获取当前秒数
		code();
		long end = System.currentTimeMillis();
		System.out.println("执行时间是:"+(end - start));
	}
	public abstract void code();
}
class SubTemplate extends Template{
    
    
	public void code(){
    
    
		for(int i = 0;i<10000;i++){
    
    
		System.out.println(i);
		}
	}
}

分析:在父类中提前写好得到时间的方法框架,当调用方法时,子类的独特方法就会应用到父类上,从而得出这个子类的运算时间。


7.更彻底的抽象:接口(interface)

       ①有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。

       ②接口(interface)是抽象方法和常量值的定义的集合。

       ③从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。

       ④实现接口类:

           class SubClass implements InterfaceA{ }

       ⑤一个类可以实现多个接口,接口也可以继承其它接口。

       ⑥接口的特点:

  • 用interface来定义。
  • 接口中的所有成员变量都默认是由public static final修饰的。
  • 接口中的所有方法都默认是由public abstract修饰的。
  • 接口没有构造器。
  • 接口采用多层继承机制。

在这里插入图片描述

代码:

public interface TestIn {
    
    
    int ID = 1;//等同public static final int ID
    void test();//等同与public abstract void void test()
}

//

public interface TeatIn1 {
    
    
    void test1();
}

///

public interface TeatIn1 {
    
    
    void test1();
}

///

/**
 * 子类继承父类,只能继承一个父类
 * 类可以实现多个接口,多个接口用,分隔
 * @author lby
 *
 */

public class TestInImpl implements TestIn,TeatIn1 {
    
    
    @Override
    public void test() {
    
    

    }

    @Override
    public void test1() {
    
    

    }
}

       ⑦实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。

/**
 * 如果类没有实现接口所有方法,这个类就要定义抽象类
 * @author lby
 *
 */
public abstract class TestInImpl1 implements TestIn{
    
    

}

       ⑧接口的主要用途就是被实现类实现。(面向接口编程)

       ⑨与继承关系类似,接口与实现类之间存在多态性

       ⑩定义Java类的语法格式:先写extends,后写implements

  < modifier> class < name> [extends < superclass>]
      [implements < interface> [,< interface>]* ] {
                < declarations>*
      }

/**
 * 如果一个类既继承父类,又实现接口
 * 那么先继承,再实现
 * @author lby
 *
 */
public class TestInImpl2 extends Person implements TestIn,TestIn1{
    
    
	@Override
	public void test() {
    
    
		
	}
	
	@Override
	public void test1() {
    
    
		
	}

}

问题:接口与抽象类很相似,为什么还要又接口?

11111111

public abstract class Test1 {
    
    
	public abstract void test();
}

class Test1Child extends Test1{
    
    

	@Override
	public void test() {
    
    
		
	}

代码分析:
此时已经创建完抽象类和在Test1Child中继承抽象类并对其重写。
若在此时我想修改父类,在抽象类中加多一个Test1

public abstract class Test1 {
    
    
	public abstract void test();
	public abstract void test1();

}

class Test1Child extends Test1{
    
    //这里就会报错

	@Override
	public void test() {
    
    
		
	}

代码分析:
此时要么把 Test1Child改为抽象类要么重写父类的抽象方法,所以就必须动用子类,但子类在后边已经用上了,出现这样的情况显然不合适。


比喻
人类是由猴子进化来的,假如要是穿越回到过去,改变了猴子(猴子多了一个眼睛),对现在的人类会造成什么影响?
从这个假设上可以看出,父类需要的稳定的抽象,如果要父类老是在改,基于这个父类的子类,子类的子类。。。这些子类都有受到影响。
有时我们又确实需要给父类增加一些方法,那么就不能直接在父类上下手,只能新建一个接口,在接口上扩展方法,其他需要的子类自行去实现接口

所以这种情况下我们就引入了接口!

在这里插入图片描述


示例

在这里插入图片描述

解答思路:

在这里插入图片描述

public abstract class Person1 {
    
    
	int age;
	String name;
	int sex;
	
	public abstract void showInfo();
}


\\\\\\

/**
 * 厨艺的接口
 * @author lby
 *
 */
public interface Cooking {
    
    
	void fry();//炒菜
//	void test();
}

\\\\\\

/**
 * 歌唱的接口
 * @author lby
 *
 */
public interface Sing {
    
    
	void singing();//唱法
}


//

/**
 * 这个是描述会唱歌的厨子是一个老师的类
 * @author lby
 *
 */
public class SCTeacher extends Person1 implements Cooking,Sing{
    
    
	String course;//教的科目
	
	public void setInfo(){
    
    
		super.age = 27;
		super.name = "王富贵";
		super.sex = 1;
		
		this.course = "数学";
	}
	
	@Override
	public void showInfo() {
    
    
		System.out.println("会唱歌的厨子的老师是信息是:");
		System.out.println(super.age);
		System.out.println(super.name);
		System.out.println(super.sex);
		System.out.println(this.course);
	}

	@Override
	public void fry() {
    
    
		System.out.println(super.name + "老师拿手的厨艺是炒菜");
	}
	
	@Override
	public void singing() {
    
    
		System.out.println(super.name + "老师擅长美声唱法");
	}

}


///\\\测试代码

		SCTeacher sct = new SCTeacher();
		
		sct.setInfo();
		sct.showInfo();
		
		sct.fry();
		sct.singing();
		

注意:与继承关系类似,接口与实现类之间存在多态性

例①:
接上边例子代码

		Cooking c = new SCTeacher();
		c.fry();
		
		Sing s = new SCTeacher();
		s.singing();

/输出
null老师拿手的厨艺是炒菜

//因为执行不了setInfo方法所以是null加上cooking中只有cooking方法所以
只有炒菜没有游泳,游泳同理

例②:

public class Test{
    
    
	public static void main(String args[]){
    
    
		Test t = new Test();
		Man m = new Man();
		t.m1(m);
		t.m2(m);
		t.m3(m);
	}
	public String m1(Runner f) {
    
     f.run(); }
	public void  m2(Swimmer s) {
    
    s.swim();}
	public void  m3(Creator a) {
    
    a.eat();}
}

接口的其他一些问题

①如果实现接口的类中没有实现接口中的全部方法,必须将此类定义为抽象类

②接口也可以继承另一个接口,使用extends关键字。

interface MyInterface{
    
    
		String s=“MyInterface”;
		public void absM1();
	}
	interface SubInterface extends MyInterface{
    
    
		public void absM2();
	}
	public class SubAdapter implements SubInterface{
    
    
		public void absM1(){
    
    System.out.println(“absM1”);}
		public void absM2(){
    
    System.out.println(“absM2”);}
	}

实现类SubAdapter必须给出接口SubInterface以及父接口MyInterface中所有方法的实现。


接口总结

抽象类是对于一类事物的高度抽象,其中既有属性也有方法
接口是对方法的抽象,也就对一系列动作的抽象

当需要对一类事物抽象的时候应该是使用抽象类,好形成一个父类
当需要对一系列的动作抽象就使用接口,需要使用这些动作的类去实现相应的接口即可


8.工厂方法(FactoryMethod)和代理模式(Proxy)

          FactoryMethod模式是设计模式中应用最为广泛的模式,在面向对象的编程中,对象的创建工作非常简单,对象的创建时机却很重要。FactoryMethod解决的就是这个问题,它通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。

----真正的开发中,都是合作开发,每个人写一部分代码,大家集合在一起就是一个项目

在这里插入图片描述

/**
 * 宝马车的产品接口
 * @author lby
 *
 */
public interface BWM {
    
    
	//产品的信息介绍
	//车的发动方式
	void showInfo();
}

/**
 * 构建具体的车的类
 * @author lby
 *
 */
class BWM3i implements BWM{
    
    //改变名字不影响程序员B的操作

	@Override
	public void showInfo() {
    
    
		System.out.println("这个是宝马3系车");
	}
	
}
class BWM5 implements BWM{
    
    

	@Override
	public void showInfo() {
    
    
		System.out.println("这个是宝马5系车");
	}
	
}
class BWM7 implements BWM{
    
    

	@Override
	public void showInfo() {
    
    
		System.out.println("这个是宝马7系车");
	}
	
}

//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

/**
 * 汽车生产工厂接口
 * @author lby
 *
 */
public interface BWMFactory {
    
    
	BWM productBWM();
}

/**
 * 实现具体的车型的生产工厂
 * @author lby
 *
 */
class BWM3Factory implements BWMFactory{
    
    

	@Override
	public BWM productBWM() {
    
    
		System.out.println("生产宝马3系车");
		System.out.println("改造3系车,定名为BWM3i型号");
		return new BWM3i();//增加一行代码,或者更改名字并不影响开发员B的工作
	}
	
}

class BWM5Factory implements BWMFactory{
    
    

	@Override
	public BWM productBWM() {
    
    
		System.out.println("生产宝马5系车");
		return new BWM5();
	}
	
}

class BWM7Factory implements BWMFactory{
    
    

	@Override
	public BWM productBWM() {
    
    
		System.out.println("生产宝马7系车");
		return new BWM7();
	}
	
}

///

public class Test2 {
    
    
	public static void main(String[] args) {
    
    
		//这是开发人员B的工作
		BWM b3 = new BWM3Factory().productBWM();
		b3.showInfo();
		
		BWM b5 = new BWM5Factory().productBWM();
		b5.showInfo();
		
		BWM b7 = new BWM7Factory().productBWM();
		b7.showInfo();
	}
}



用的这部分人与之前写的那部分人互不冲突,这就是工厂模式。通过工厂把new对象隔离了,通过产品的接口可以接收不同实际产品的实现类,实例的类名改变,不影响其他操作人员的编程

9.类的成员之五:内部类

    在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。

    Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
            Inner class的名字不能与包含它的类名相同;

    Inner class可以使用外部类的私有数据,因为它是外部类的成员,同一个类的成员之间可相互访问。而外部类要访问内部类中的成员需要:内部类.成员或者内部类对象.成员。

    分类:成员内部类(static成员内部类和非static成员内部类)
局部内部类(不谈修饰符)、匿名内部类

public class Test3 {
    
    
	int i;
	public int z;
	private int k;
	
	class A{
    
    //这就是内部类
		int i;
		public void setTest3Fileds(){
    
    //用内部类去定义i、z、k
			Test3.this.i = 1;
			Test3.this.z = 2;
			Test3.this.k = 3;
		}
		
		public void set(){
    
    \\内部类定义本内部类的i
			this.i = 10;
		}
	}


public void setInfo(){
    
    
		new A().setTest3Fileds();//外部的类要用自己的内部类的方法,得先new内部类的对象
	}

public void showInfo(){
    
    
		System.out.println(this.i);
		System.out.println(this.z);
		System.out.println(this.k);
	}
}

	public static void main(String[] args) {
    
    
		Test3 t = new Test3();
		t.setInfo();
		t.showInfo();
	}

内部类特性

    Inner class作为类的成员:

  • 内部类可以定义多个,内部类可以声明为final的
  • 和外部类不同,Inner class可声明为private或protected;
  • Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;

    Inner class作为类:

  • 可以声明为abstract类 ,因此可以被其它的内部类继承

【注意】非static的内部类中的成员不能声明为static的,只有在外部类或static的内部类中才可声明static成员。

	//如果内部类是static的,就不能使用外部类的非static的成员
	static class B{
    
    
		
	}
	
	abstract class C{
    
    
		
	}
	
	class D extends C{
    
    
		
	}
	
	public void setInfo(){
    
    
		new A().setTest3Fileds();//外部的类要用自己的内部类的方法,得先new内部类的对象
	}


问题?内部类有什么用
--------内部类主要是解决java不能多重继承的问题

现在类A想同时获得类B和类C的方法,并且重写
可以使用内部类来变相的实现类的多重继承,可以同时继承多个类

public class Test4 {
    
    
	public static void main(String[] args) {
    
    
		A a = new A();
		a.testB();
		a.testC();
	}

}

/**
 * 现在类A想同时获得类B和类C的方法,并且重写
 * 可以使用内部类来变相的实现类的多重继承,可以同时继承多个类
 * @author lby
 *
 */
class A{
    
    
	
	public void testB(){
    
    
		new InnerB().testB();
	}
	
	public void testC(){
    
    
		new InnerC().testC();
	}
	
	private class InnerB extends B{
    
    
		@Override
		public void testB() {
    
    
			System.out.println("这是重写之后的testB方法");
		}
	}
	private class InnerC extends C{
    
    
		@Override
		public void testC() {
    
    
			System.out.println("这是重写之后的testC方法");
		}
	}
}

class B{
    
    
	public void testB(){
    
    
		
	}
}

class C{
    
    
	public void testC(){
    
    
		
	}
}

///

输出结果:
这是重写之后的testB方法
这是重写之后的testC方法

面向对象内容总结

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/maikotom/article/details/114757062