JAVA学习笔记9,抽象类和接口及内部类
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了JAVA学习笔记9,抽象类和接口及内部类,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含11288字,纯文字阅读大概需要17分钟。
内容图文
![JAVA学习笔记9,抽象类和接口及内部类](/upload/InfoBanner/zyjiaocheng/606/1baed8689afa4586a321048ad81094fe.jpg)
第九章 抽象类和接口及内部类
一 抽象类和抽象方法
1.1 抽象类
用abstract关键字来修饰一个类,这个类叫做抽象类。
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
1.2 抽象方法
用abstract来修饰一个方法,该方法叫做抽象方法。
- 只有方法的声明,没有方法的实现。以分号结束
- 含有抽象方法的类必须被声明为抽象类。反之,抽象类中可以没有抽象方法的。
- 抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类,需要用abstract修饰。
1.3 abstract使用注意事项
- 不能用abstract修饰变量、代码块、构造器;
- 不能用abstract修饰私有方法、静态方法、final的方法、final的类。
1.4 练习
- 编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元。
- 定义一个Employee类,该类包含:private成员变量name,number,birthday,其中birthday 为MyDate类的对象;abstract方法earnings();toString()方法输出对象的name,number和birthday。
- MyDate类包含:private成员变量year,month,day ;
toDateString()方法返回日期对应的字符串:xxxx年xx月xx日 - 定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:private成员变量monthlySalary;实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输出员工类型信息及员工的name,number,birthday。
- 参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:private成员变量wage和hour;实现父类的抽象方法earnings(),该方法返回wage*hour值;toString()方法输出员工类型信息及员工的name,number,birthday。
- 创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。
import java.util.Scanner;
public class Test3 {
public static void main(String[] args) {
Employee[] emps= new Employee[]{
new SalariedEmployee("tom", new MyDate(1999, 2, 11), 6000),
new SalariedEmployee("john", new MyDate(1992, 5, 28), 7000),
new HourlyEmployee("wang", new MyDate(2000, 8, 1), 60, 31)
};
Scanner scan = new Scanner(System.in);
System.out.print("输入月份:");
int m=scan.nextInt();
for (int i = 0; i < emps.length; i++) {
System.out.println(emps[i]);
if (m==emps[i].getBirthday().getMonth()) {
System.out.println("今天是你的生日,增加工资100元");
}
}
scan.close();
}
}
class HourlyEmployee extends Employee {
private double wage;
private int hours;
public HourlyEmployee(String name, MyDate birthday, double wage, int hours) {
super(name, birthday);
this.wage = wage;
this.hours = hours;
}
@Override
public double earnings() {
return wage*hours;
}
@Override
public String toString() {
return "HourlyEmployee [名字:"+getName()+" 员工号:"+getNumber()+" 薪水:" + earnings() +" 生日:"+getBirthday() +"]";
}
}
class SalariedEmployee extends Employee {
private double monthlySalary;
@Override
public double earnings() {
return monthlySalary;
}
public SalariedEmployee(String name, MyDate birthday, double monthlySalary) {
super(name,birthday);
this.monthlySalary = monthlySalary;
}
@Override
public String toString() {
return "SalariedEmployee [名字:"+getName()+" 员工号:"+getNumber()+" 薪水:" + earnings() +" 生日:"+getBirthday() +"]";
}
}
class MyDate{
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
@Override
public String toString() {
return year+"年" + month + "月" + day + "日";
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
}
abstract class Employee{
private String name;
private int number;
private MyDate birthday;
public abstract double earnings();
private static int total=0;
private static int id=1000;
public Employee(String name,MyDate birthday) {
this.name = name;
this.number = (total++)+id;
this.birthday = birthday;
}
public String getName() {
return name;
}
public int getNumber() {
return number;
}
public MyDate getBirthday() {
return birthday;
}
public void setName(String name) {
this.name = name;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
}
二 接口interface
接口(interface)是抽象方法和常量值定义的集合。
2.1 接口的特点:
- 用interface来定义。
- 接口中的所有成员变量都默认是由public static final修饰的。
- 接口中的所有抽象方法都默认是由public abstract修饰的。
- 接口中没有构造器。
- 接口采用多继承机制。
2.2 接口语法格式
先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }
interface Person{
String name="人";//省略public final static
public final static String con="中国";
void eat();//省略public abstract
public abstract void walk();
}
interface Student extends Person{
void study();
}
class Pupil implements Student{
@Override
public void eat() {
}
@Override
public void walk() {
}
@Override
public void study() {
}
}
2.3 注意事项
- 一个类可以实现多个接口,接口也可以继承其它接口。
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
- 接口的主要用途就是被实现类实现。(面向接口编程)
- 与继承关系类似,接口与实现类之间存在多态性
- 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。
2.4 接口和抽象类之间的对比
- 抽象类
- 包含抽象方法的类
- 由构造方法、抽象方法、普通方法、常量、变量组成
- 子类继承抽象类(extends)
- 抽象类可以实现多个接口
- 抽象类有单继承的局限
- 常见的设计模式有模板方法
- 接口
- 主要是抽象方法和全局常量的集合
- 由常量、抽象方法、(jdk8.0:默认方法、静态方法)构成
- 子类实现接口(implements)
- 接口不能继承抽象类,但允许继承多个接口
- 常见的设计模式有简单工厂、工厂方法、代理模式
- 相同点
- 都通过对象的多态性产生实例化对象
- 使用建议
- 如果抽象类和接口都可以使用的话,优先使用接口,避免单继承的局限
在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,
要么实现接口。
2.5 interface Java 8新特性
- 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
- 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:Java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。
- 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现接口冲突。
- 解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
- 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
- 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现接口冲突。
2.6 练习
- 定义一个接口CompareObject用来实现两个对象的比较。
- 若返回值是 0 , 代表相等; 若为正数,代表当
前对象大;负数代表当前对象小 - 定义一个Circle类,声明redius属性,提供getter和setter方法
- 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
- 定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo
方法比较两个类的半径大小。 - 思考 :参照上述做法定义矩形类 Rectangle 和 ComparableRectangle类,在ComparableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小。
public class Test4 {
public static void main(String[] args) {
ComparableCircle cc1 = new ComparableCircle(1.5);
ComparableCircle cc2 = new ComparableCircle(1.5);
System.out.println(cc1.compareTo(cc2));//0
}
}
class ComparableCircle extends Circle implements CompareObject {
@Override
public int compareTo(Object o) {
if(o == null)
throw new RuntimeException();
if(this == o)
return 0;
if(o instanceof ComparableCircle){
ComparableCircle cc=(ComparableCircle) o;
if (cc.getRedius()==this.getRedius()){
return 0;
}else if(this.getRedius()<cc.getRedius()){
return -1;
}else{
return 1;
}
}
throw new RuntimeException();
}
public ComparableCircle(double redius) {
super(redius);
}
}
class Circle{
private double redius;
public Circle() {
}
public Circle(double redius) {
this.redius = redius;
}
public double getRedius() {
return redius;
}
public void setRedius(double redius) {
this.redius = redius;
}
}
interface CompareObject{
int compareTo(Object o);
}
三 内部类
3.1 内部类定义
- 一个类A的定义位于另一个类B的内部,前者A称为内部类,后者B称为外部类。
- Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
- Inner class的名字不能与包含它的外部类类名相同;
3.2 分类
- 成员内部类(static成员内部类和非static成员内部类)
- 作为外部类的成员
- 和外部类不同,Inner class还可以声明为private或protected;
- 可以调用外部类的结构
- Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;
- 作为一个类
- 可以在内部定义属性、方法、构造器等结构
- 可以声明为abstract类 ,因此可以被其它的内部类继承
- 可以声明为final的
- 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
- 注意
- 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
- 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
- 成员内部类可以直接使用外部类的所有成员,包括私有的数据
- 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
- 作为外部类的成员
public class Test5 {
private int num = 123;
public class Inner {
private int num = 456;
public void mb(int num) {
System.out.println(num); // 局部变量
System.out.println(this.num); // 内部类对象的属性
System.out.println(Test5.this.num); // 外部类对象属性s
}
}
public static void main(String args[]) {
Test5 out = new Test5();
Test5.Inner inner = out.new Inner();
inner.mb(110);
}
}
- 局部内部类(不谈修饰符)、匿名内部类
- 局部内部类的申明
class 外部类{
方法(){//方法内
class 局部内部类{ }
}
{//代码块内
class 局部内部类{ }
}
}
- 局部内部类使用
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
- 但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
- 局部内部类的特点
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号,以及数字编号。
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
- 局部内部类可以使用外部类的成员,包括私有的。
- 局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
- 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private
- 局部内部类不能使用static修饰,因此也不能包含静态成员
- 匿名内部类
- 不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
- 格式:
new 父类构造器(实参列表)|实现接口(){ //匿名内部类的类体部分 }
- 匿名内部类的特点
- 匿名内部类必须继承父类或实现接口
- 匿名内部类只能有一个对象
- 匿名内部类对象只能使用多态形式引用
public class Test5 {
public static void main(String args[]) {
Test5 test = new Test5();
test.getRunnable1().run();//我是局部内部类
test.gRunnable2().run();//我是匿名局部内部类
}
public Runnable getRunnable1() {
class Runn implements Runnable{
@Override
public void run() {
System.out.println("我是局部内部类");
}
}
return new Runn();
}
public Runnable gRunnable2() {
return new Runnable(){
@Override
public void run() {
System.out.println("我是匿名局部内部类");
}
};
}
}
Runnable{
@Override
public void run() {
System.out.println("我是局部内部类");
}
}
return new Runn();
}
public Runnable gRunnable2() {
return new Runnable(){
@Override
public void run() {
System.out.println("我是匿名局部内部类");
}
};
}
}
内容总结
以上是互联网集市为您收集整理的JAVA学习笔记9,抽象类和接口及内部类全部内容,希望文章能够帮你解决JAVA学习笔记9,抽象类和接口及内部类所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。