day12

抽象类与抽象方法

类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

 abstract关键字的使用
 * 1.abstract:抽象的
 * 2.abstract可以用来修饰的结构:类、方法
 * 
 * 3. abstract修饰类:抽象类
 * 		> 此类不能实例化
 *      > 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
 *      > 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
 * 
 * 
 * 4. abstract修饰方法:抽象方法
 * 		> 抽象方法只有方法的声明,没有方法体
 * 		> 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
 *      > 若子类重写了父类中的所有的抽象方法后,此子类方可实例化
 *        若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
package cap15abstract;

public class AbstractTest {
	public static void main(String[] args) {
		Person p = new Student();
		p.eat();//学生多吃点,在长身体~~~
		p.walk();//走路~~~
	}
}
abstract class Person{
	public void walk() {
		System.out.println("走路~~~");
	}
	public abstract void eat();
	
}
class Student extends Person{

	@Override
	public void eat() {
		System.out.println("学生多吃点,在长身体~~~");
	}
	
}

abstract使用上的注意点:

  1. abstract不能用来修饰:属性、构造器等结构
  2. abstract不能用来修饰私有方法、静态方法、final的方法、final的类

抽象类的匿名子类对象

public class AbstractTest {
	public static void main(String[] args) {
		test(new Person() {

			@Override
			public void eat() {
				System.out.println("多吃一点呀~~~~~~");
			}
			
		});
	}
	public static void test(Person p) {
		p.eat();
		p.walk();
	}
}
abstract class Person{
	public void walk() {
		System.out.println("走路~~~");
	}
	public abstract void eat();
}

模板方法设计模式

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

当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。

换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式

public class TemplateMethods {
	public static void main(String[] args) {
		new A() {

			@Override
			public void run02() {
				System.out.println("中");
				
			}
			
		}.test();
		System.out.println("===");
		new A() {

			@Override
			public void run02() {
				System.out.println("中~~~~~~");
				
			}
			
		}.test();
	}
}
abstract class A{
	public void run01(){
		System.out.println("前");
	}
	public abstract void run02();
	public void run03() {
		System.out.println("后");
	}
	public void test() {
		run01();
		run02();
		run03();
	}
}
/*
前
中
后
===
前
中~~~~~~
后
*/

练习:

编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个
Employee对象的生日,则将该雇员的工资增加100元。
实验说明:
(1)定义一个Employee类,该类包含:private成员变量name,number,birthday,其中birthday 为MyDate类的对象;abstract方法earnings();toString()方法输出对象的name,number和birthday。 

(2)MyDate类包含:private成员变量year,month,day ;toDateString()方法返回日期对应的字符串:xxxx年xx月xx日 

(3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:private成员变量monthlySalary;实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输
出员工类型信息及员工的name,number,birthday。

(4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:private成员变量wage和hour;实现父类的抽象方法earnings(),该方法返回wage*hour值;toString()方法输出员工类型信息及员工的name,number,birthday。 

(5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本
月是某个Employee对象的生日,还要输出增加工资信息。
package cap15abstractexe;
/**
 * 定义一个Employee类,该类包含:
private成员变量name,number,birthday,其中birthday 为MyDate类的对象;
abstract方法earnings();
toString()方法输出对象的name,number和birthday。
 * @author xiao-liu
 *
 */
public abstract class Employee {
	private String name;
	private int number;
	private MyDate birthday;
	public Employee(String name,int number,MyDate birthday) {
		this.name = name;
		this.number = number;
		this.birthday = birthday;
	}
	public abstract double earnings();
	
	public String getName() {
		return name;
	}

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

	public int getNumber() {
		return number;
	}

	public void setNumber(int number) {
		this.number = number;
	}

	public MyDate getBirthday() {
		return birthday;
	}

	public void setBirthday(MyDate birthday) {
		this.birthday = birthday;
	}

	@Override
	public String toString() {
		return "[name=" + name + ", number=" + number + ", birthday=" + birthday.toDateSting() + "]";
	}
	
}
package cap15abstractexe;

/*
 * 参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的
员工处理。该类包括:
private成员变量wage和hour;
实现父类的抽象方法earnings(),该方法返回wage*hour值;
toString()方法输出员工类型信息及员工的name,number,birthday
 */
public class HourlyEmployee extends Employee {
	private double wage;
	private int hour;

	public HourlyEmployee(String name, int number, MyDate birthday) {
		super(name, number, birthday);
	}

	public HourlyEmployee(String name, int number, MyDate birthday, double wage, int hour) {
		this(name, number, birthday);
		this.hour = hour;
		this.wage = wage;
	}

	@Override
	public double earnings() {

		return wage * hour;
	}

	@Override
	public String toString() {
		return "HourlyEmployee " + super.toString();
	}

	public double getWage() {
		return wage;
	}

	public void setWage(double wage) {
		this.wage = wage;
	}

	public int getHour() {
		return hour;
	}

	public void setHour(int hour) {
		this.hour = hour;
	}
}
package cap15abstractexe;
/**
 * MyDate类包含:
private成员变量year,month,day ;
toDateString()方法返回日期对应的字符串:xxxx年xx月xx日
 * @author xiao-liu
 *
 */
public class MyDate {
	private String year;
	private String month;
	private String day;
	
	public MyDate(String name,String month,String day) {
		this.year = year;
		this.month = month;
		this.day = day;
	}
	
	public String getYear() {
		return year;
	}

	public void setYear(String year) {
		this.year = year;
	}

	public String getMonth() {
		return month;
	}

	public void setMonth(String month) {
		this.month = month;
	}

	public String getDay() {
		return day;
	}

	public void setDay(String day) {
		this.day = day;
	}

	public String toDateSting() {
		return year+"年"+month+"月"+day+"日";
	}
}
package cap15abstractexe;
/**
 * 定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处
理。该类包括:private成员变量monthlySalary;
实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输
出员工类型信息及员工的name,number,birthday。
 * @author xiao-liu
 *
 */
public class SalariedEmployee extends Employee{
	private double monthSalary;
	public SalariedEmployee(String name, int number, MyDate birthday,double monthSalary) {
		super(name, number, birthday);
		this.monthSalary = monthSalary;
	}
	@Override
	public double earnings() {
		return monthSalary;
	}
	@Override
	public String toString() {
		
		return "SalariedEmployee"+super.toString();
	}
}
package cap15abstractexe;

import java.util.Scanner;

/*
 * 定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各
类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类
型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本
月是某个Employee对象的生日,还要输出增加工资信息。
 */
public class PayrollSystem {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入当前月份:");
		String target = sc.next();
		Employee emps[] = new Employee[2];
		emps[0] = new HourlyEmployee("张三",1001,new MyDate("1999","2","21"),50,240);
		emps[1] = new SalariedEmployee("李四",1002,new MyDate("1999","3","22"),10000);
		for(int i = 0;i < emps.length;i++) {
			System.out.println(emps[i]);
			String month = emps[i].getBirthday().getMonth();
			if(target.equals(month)) {
				System.out.println(emps[i].getName()+"过生日,口头奖励100元");
			}
		}
	}
}
接口

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

有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。

接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。

* 接口的使用
 * 1.接口使用interface来定义
 * 2.Java中,接口和类是并列的两个结构
 * 3.如何定义接口:定义接口中的成员
 * 		
 * 		3.1 JDK7及以前:只能定义全局常量和抽象方法
 * 			>全局常量:public static final的.但是书写时,可以省略不写
 * 			>抽象方法:public abstract的
 * 			
 * 		3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)
 * 
 * 4. 接口中不能定义构造器的!意味着接口不可以实例化
 * 
 * 5. Java开发中,接口通过让类去实现(implements)的方式来使用.
 *    如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
 *    如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
 *    
 * 6. Java类可以实现多个接口   --->弥补了Java单继承性的局限性
 *   格式:class AA extends BB implements CC,DD,EE
 *   
 * 7. 接口与接口之间可以继承,而且可以多继承
 * 
 * *******************************
 * 8. 接口的具体使用,体现多态性
 * 9. 接口,实际上可以看做是一种规范
 * 
 * 面试题:抽象类与接口有哪些异同?
public class ImplemtsTest {
	public static void main(String[] args) {
		Person stu = new Student();
		stu.eat();
		stu.run();
	}
}
interface Person{
	public static final int A = 1001;
	public void run();
	public void eat();
}
class Student implements Person{

	@Override
	public void run() {
		System.out.println("小学生要多运动");
	}

	@Override
	public void eat() {
		System.out.println("小学生多吃一点");
	}
	
}

代理模式

代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。

public class ProxyTest {
	public static void main(String[] args) {
		C c = new C(new B());
		c.eat();
	}
}
interface A{
	public void eat();
}
class B implements A{//被代理的类

	@Override
	public void eat() {
		// TODO Auto-generated method stub
		System.out.println("bbbb");
	}
}
class C implements A{//代理类
	private A a;
	public C(A a) {
		this.a = a;
	}
	public void check() {
		System.out.println("做一些检查");
	}
	@Override
	public void eat() {
		check();
		a.eat();
		
	}
	
}

应用场景

安全代理:屏蔽对真实角色的直接访问

远程代理:通过代理类处理远程方法调用(RMI)

延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。

分类

静态代理(静态定义代理类)

动态代理(动态生成代理类) JDK自带的动态代理,需要反射等知识

接口与抽象类之间的对比

NO区别点抽象类接口
1定义包含抽象方法的类主要是抽象方法和全局常量的集合
2组成构造方法,抽象方法,普通方法,常量,变量常量,抽象方法,(jdk1.8:默认方法,静态方法)
3使用子类继承抽象类子类实现接口
4关系抽象类可以实现多个接口接口不能继承抽象类,但允许继承多个接口
5常见设计模式模板方法简单工厂,工厂方法,代理模式
6对象都通过对象的多态性产生实例化对象(<—和前面一块的)
7局限抽象类有单继承的局限接口没有此局限
8实际作为一个模板是作为一个标准或是表示一种能力
9选择如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限(<—和前面一块的)

Java 8中关于接口的改进

Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。

**静态方法:**使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。

**默认方法:**默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法

接口中的默认方法

若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接时,会出现:接口冲突。

解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。

若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:**类优先原则。**接口中具有相同名称和参数的默认方法会被忽略。

package cap1Impl;

public class InterfaceTest {
	public static void main(String[] args) {
		C1 c1 = new C1();
		c1.help();
	}
}
class C1 implements A1,B1{

	@Override
	public void help() {
		System.out.println("我该救谁呢???~~~~");
		A1.super.help();
		B1.super.help();
	}
	
}
interface A1{
	public default void help() {
		System.out.println("help me A1~~~");
	}
}
interface B1{
	public default void help() {
		System.out.println("help me B1~~~");
	}
}
内部类
* 1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
 * 
 * 2.内部类的分类:成员内部类(静态、非静态)  vs 局部内部类(方法内、代码块内、构造器内)
 * 
 * 3.成员内部类:
 * 		一方面,作为外部类的成员:
 * 			>调用外部类的结构
 * 			>可以被static修饰
 * 			>可以被4种不同的权限修饰
 * 
 * 		另一方面,作为一个类:
 * 			> 类内可以定义属性、方法、构造器等
 * 			> 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
 * 			> 可以被abstract修饰
 * 
 * 
 * 4.关注如下的3个问题
 *   4.1 如何实例化成员内部类的对象
 *   4.2 如何在成员内部类中区分调用外部类的结构
package cap15nbulei;

import cap15nbulei.Person.Bird;
import cap15nbulei.Person.Dog;

public class InnerTest {
	public static void main(String[] args) {
		Dog dog = new Person.Dog();//静态
		dog.show();
		Person p = new Person();
		Bird bird = p.new Bird();//非静态
		bird.display("黄鹂");
	}
}
class Person{
	String name = "小明";
	int age;
	public void eat() {
		System.out.println("吃饭");
	}
	
	static class Dog{
		String name;
		int age;
		public void show() {
			System.out.println("是一条狗");
		}
	}
	class Bird{
		String name = "杜鹃";
		public Bird(){
			
		}
		public void sing(){
			System.out.println("我是一只小小鸟");
			Person.this.eat();//调用外部类的非静态属性
			eat();
			System.out.println(age);
		}
		public void display(String name){
			System.out.println(name);//方法的形参
			System.out.println(this.name);//内部类的属性
			System.out.println(Person.this.name);//外部类的属性
		}
	}
}
package cap15nbulei;

public class InnerTest1 {
	public Comparable getComparable() {
//方式一
//		class MyComparable implements Comparable{
//
//			@Override
//			public int compareTo(Object o) {
//				
//				return 0;
//			}
//			
//		}
//		return new MyComparable();
        //方式二
		return new Comparable() {

			@Override
			public int compareTo(Object o) {
				// TODO Auto-generated method stub
				return 0;
			}
			
		};
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值