抽象类与接口
一、抽象类
1. 抽象类基本定义
抽象类使用abstract class定义,并且其中的方法也可利用abstract定义若干个抽象方法,这样抽象类的子类必须再继承抽象类时强制全部覆写方法
import java.util.Date;
public class AbstractDemo {
public static void main(String[] args) {
Message msg = new DataMessage();
msg.setType("信息类型");
System.out.println(msg.getType());
DataMessage datamsg = (DataMessage) msg;
System.out.println(datamsg.getConnect());
}
}
abstract class Message{
private String type;
public abstract String getConnect();
public void setType(String type){
this.type = type;
}
public String getType(){
return this.type;
}
}
class DataMessage extends Message{
@Override
public String getConnect(){
return "数据库链接信息!";
}
}
2. 抽象类相关说明
- 抽象类必须由子类继承,所以不允许使用final关键字定义抽象类或者抽象方法
- 抽象类中可以定义普通方法和成员属性,为了初始化成员属性, 还提供了构造方法。子类继承抽象类会默认调用父类的无参构造,如果没有提供无参构造,则子类必须通过super()形式调用指定参数的构造方法
public class AbstractDemo {
public static void main(String[] args) {
Message msg = new DataMessage("信息类型");
System.out.println(msg.getType());
DataMessage datamsg = (DataMessage) msg;
System.out.println(datamsg.getConnect());
}
}
abstract class Message{
private String type;
public Message(String type){
this.type = type;
}
public abstract String getConnect();
public void setType(String type){
this.type = type;
}
public String getType(){
return this.type;
}
}
class DataMessage extends Message{
public DataMessage(String type){
super(type);
}
@Override
public String getConnect(){
return "数据库链接信息!";
}
}
- 抽象类中允许没有抽象方法,即使没有抽象方法,也无法直接使用关键字new直接实例化抽象类
- 抽象类可以提供static方法,并且该类方法不受到抽象类实例化对象的限制
public class AbstractDemo {
public static void main(String[] args) {
Message msg = Message.getInstance();
System.out.println(msg.getInfo());
}
}
abstract class Message{
public abstract String getInfo();
public static Message getInstance(){// 静态方法
System.out.println("打印抽象类中的静态方法");
return new DataMessage();
}
}
class DataMessage extends Message{
@Override
public String getInfo(){
return "打印信息";
}
}
3. 模板设计模式
public class AbstractDemo {
public static void main(String[] args) {
Action robotAction = new Robot();
robotAction.command(Action.EAT);
}
}
abstract class Action{
public static final int EAT = 1;
public static final int SLEEP = 5;
public static final int WORK = 10;
public abstract void eat();
public abstract void sleep();
public abstract void work();
public void command(int code){
switch (code) {
case EAT:{
this.eat();
break;
}
case SLEEP:{
this.sleep();
break;
}
case WORK:{
this.work();
break;
}
case EAT+SLEEP+WORK:{
this.eat();
this.work();
this.sleep();
break;
}
}
}
}
class Robot extends Action{
@Override
public void eat(){
System.out.println("机器人要充电");
}
@Override
public void sleep(){}
@Override
public void work(){
System.out.println("机器人在干活");
}
}
class pig extends Action{
@Override
public void eat(){
System.out.println("杂食动物");
}
@Override
public void sleep(){
System.out.println("随地睡觉");
}
@Override
public void work(){}
}
写一个抽象类,然后以此为模板,创建不同的子类对象,根据需要对方法进行覆写
二、包装类
1. 基础数据类型的包装
public class AbstractDemo {
public static void main(String[] args) {
Object obj = new Int(10);// 向上转型,将10包装在Int类中
int x = ((Int) obj).IntValue();//向下转型为Int类,输出数字
System.out.println(x*2);
}
}
class Int{// 包装类
private int data;
public Int(int data){
this.data = data;
}
public int IntValue(){
return this.data;
}
}
利用这种处理就可以用Object进行基本数据类型的接受,从而实现参数的统一化处理
Java设计了8个包装类,可分为了两种类型:
- 对象型包装类(Object直接子类):Boolean, Charachter
- 数值型包装类(Number直接子类):Byte,Short,Integer,Long,Float,Double
1. 装箱与拆箱
以Double和double为例实现转换
public class AbstractDemo {
public static void main(String[] args) {
Double obj = new Double(12.5);
double num = obj.doubleValue();
System.out.println(num*num);
}
}
以Boolean和boolean为例实现转换
public class AbstractDemo {
public static void main(String[] args) {
Boolean obj = new Boolean(true);
boolean flag = obj.booleanValue();
System.out.println(flag);
}
}
java在1.5之后实现了自动装箱功能,上面的写法就逐渐弃用了
public class AbstractDemo {
public static void main(String[] args) {
Integer obj = 10;
int num = obj;
obj++;
System.out.println(num * obj);
}
}
使用Object接收浮点数据
public class AbstractDemo {
public static void main(String[] args) {
Object obj = 12.5;// 利用Object接受一个浮点数,此时就是利用Object装箱
double num = (Double) obj; // 想要拆箱,必须要向下转型为Double类型
System.out.println(num*2);
}
}
Integer自动装箱的数据比较问题
public class AbstractDemo {
public static void main(String[] args) {
Integer x = new Integer(10);
Integer y = 10;
Integer z = 10;
System.out.println(x == y);//false
System.out.println(x == z);//false
System.out.println(z == y);//true
System.out.println(x.equals(y));//true
}
}
2. 装箱数据的==和euqals
装箱的数据,判断相等,如果数据范围在-128~127之间,会自动实现堆内存的引用,可以使用==判断
超出这个范围的数据,需要使用equals来实现相等比较
public class AbstractDemo {
public static void main(String[] args) {
Integer x = 100;
Integer y = 100;
Integer i = 130;
Integer j = 130;
System.out.println(x == y);// true
System.out.println(i == j);// false
System.out.println(i.equals(j)); // true
}
}
3. 数据类型转换
java程序中的所有输入内容都会利用String类型表述,所以需要通过包装类实现各种数据类型的转换,如:
- Integer类:
public static int parseInt(String s)
- Boolean类:
public static boolean parseBoolean(String s)
public class AbstractDemo {
public static void main(String[] args) {
String s = new String("1");
int num = Integer.parseInt(s);
System.out.println(num*21);
}
}
public class AbstractDemo {
public static void main(String[] args) {
boolean flagA = Boolean.parseBoolean(new String("true"));
System.out.println(flagA);// 输出 true
boolean flagB = Boolean.parseBoolean(new String("孙笑川"));
System.out.println(flagB);// 字符不是true或false,都输出为false
}
}
还可以将基本的数据类型转换为字符串类型,利用String类中的valueOf()
方法
public class AbstractDemo {
public static void main(String[] args) {
int num = 10;
String str = String.valueOf(num);
System.out.println(str);
}
}
三、接口
接口属于一种特殊的类,通过interface定义,在接口中可以定义全局常量、抽象方法(public方法)、default方法和static方法
- 接口需要被子类实现,子类利用implements关键字可以实现多个接口
- 子类如果不是抽象类,那么一定要覆写接口中的全部抽象方法
- 接口对象可以利用子类对象的向上转型进行实例化
关键字顺序 class 子类 [extends 父类] [implements 多个接口]
1. 接口的基本定义
使用接口
public class AbstractDemo {
public static void main(String[] args) {
IMessage msg = new Message();// 向上转型
msg.printInfo();// 这个方法使用的是接口中的方法,但是方法体是子类中的方法体
}
}
interface IMessage{
public static final String INFO= "接口";
public void printInfo();
}
class Message implements IMessage{
@Override
public void printInfo(){
System.out.println("FUCK!");
}
}
子类实现多个父接口
public class AbstractDemo {
public static void main(String[] args) {
IMessage msg = new Message();
msg.printInfo();
}
}
interface IMessage{
public static final String INFO= "接口";
public abstract void printInfo();
}
interface IChannel{
public abstract boolean connect();
}
class Message implements IMessage, IChannel{
@Override
public void printInfo(){
if (connect()){
System.out.println("FUCK!");
}
}
@Override
public boolean connect(){
return true;
}
}
public class AbstractDemo {
public static void main(String[] args) {
IMessage msg = new Message();
msg.printInfo();
IChannel chl = (IChannel) msg;// 强制转换为IChannel接口实例
System.out.println(chl.connect());// 才能调用该接口的connect方法
}
}
interface IMessage{
public static final String INFO= "接口";
public void printInfo();
}
interface IChannel{
public abstract boolean connect();
}
class Message implements IMessage, IChannel{
@Override
public void printInfo(){
if (connect()){
System.out.println("FUCK!");
}
}
@Override
public boolean connect(){
return true;
}
}
完整定义
interface IMessage{
public static final String INFO = "孙笑川";
public abstract String getInfo();
}
简化定义
interface IMessage{
String INFO= "孙笑川";
String getInfo();
}
子类继承抽象类同时实现接口
public class AbstractDemo {
public static void main(String[] args) {
IMessage msg = new Message();
System.out.println(msg.getInfo());
}
}
interface IMessage{
String INFO= "接口";
String getInfo();
}
interface IChannel{
boolean connect();
}
abstract class DataBaseInfo{
public abstract boolean databaseconnect();
}
class Message extends DataBaseInfo implements IMessage, IChannel{
@Override
public boolean connect(){
return true;
}
@Override
public boolean databaseconnect() {
return true;
}
@Override
public String getInfo() {
if (connect()){
if (databaseconnect()){
return "连接成功!";
} else {
return "失败!";
}
} else{
return "失败!";
}
}
}
定义抽象类的过程中所有的抽象方法必须有abstract关键字定义
使用extends继承多个父接口
public class AbstractDemo {
public static void main(String[] args) {
IMessage msg = new Message();
System.out.println(msg.getInfo());
}
}
interface IMessage{
public static final String INFO= "接口";
public abstract String getInfo();
}
interface IChannel{
public abstract boolean connect();
}
interface IService extends IMessage,IChannel{// 使用extends实现接口继承接口
public abstract boolean databaseconnect();
}
class Message implements IService{// 继承一个接口,实现所有的抽象方法
@Override
public boolean connect(){
return true;
}
@Override
public boolean databaseconnect() {
return true;
}
@Override
public String getInfo() {
if (connect()){
if (databaseconnect()){
return "连接成功!";
} else {
return "失败!";
}
} else{
return "失败!";
}
}
}
2. 接口定义加强
在接口中使用default定义普通方法
public class AbstractDemo {
public static void main(String[] args) {
IMessage msg = new Message();
msg.printInfo();// 默认方法被继承,直接使用
msg.printStaticInfo();// 静态方法直接调用
System.out.println(msg.getInfo());
}
}
interface IMessage{
public static final String INFO= "接口";
public abstract String getInfo();
public default void printInfo(){// 定义了普通方法,可以直接被子类继承,但需要实例化接口才能使用
System.out.println("默认方法");
};
public static default void printStaticInfo(){// 使用static定义,可以直接利用接口名称调用
System.out.println("静态方法");
}
}
interface IChannel{
public abstract boolean connect();
}
interface IService extends IMessage,IChannel{
public abstract boolean databaseconnect();
}
class Message implements IService{
@Override
public boolean connect(){
return true;
}
@Override
public boolean databaseconnect() {
return true;
}
@Override
public String getInfo() {
if (connect()){
if (databaseconnect()){
return "连接成功!";
} else {
return "失败!";
}
} else{
return "失败!";
}
}
}
使用过渡抽象类,继承多个接口,统一管理接口
public class AbstractDemo {
public static void main(String[] args) {
IMessage msg = new Message();
msg.printInfo();
System.out.println(msg.getInfo());
}
}
interface IMessage{
public static final String INFO= "接口";
public abstract String getInfo();
public default void printInfo(){
System.out.println("默认方法");
};
}
interface IChannel{
public abstract boolean connect();
}
interface IService{
public abstract boolean databaseconnect();
}
abstract class AbstractClass implements IMessage,IChannel,IService {
// 过渡抽象类统一管理多个接口
}
class Message extends AbstractClass{
@Override
public boolean connect(){
return true;
}
@Override
public boolean databaseconnect() {
return true;
}
@Override
public String getInfo() {
if (connect()){
if (databaseconnect()){
return "连接成功!";
} else {
return "失败!";
}
} else{
return "失败!";
}
}
}
3. 定义接口标准
public class AbstractDemo {
public static void main(String[] args) {
Computer labtap = new Computer();
labtap.plugin(new Keyboard());
}
}
class Computer{
public void plugin(IUSB iusb){// 插入有USB接口的对象
if (iusb.check()){
iusb.work();
}else{
System.out.println("故障!");
}
}
}
interface IUSB{// 标准接口
public abstract boolean check();
public abstract void work();
}
class Keyboard implements IUSB{
public boolean check(){
return true;
}
public void work(){
System.out.println("键盘连接成功");
}
}
class Mouse implements IUSB{
public boolean check(){
return true;
}
public void work(){
System.out.println("鼠标连接成功");
}
}
4. 工厂设计模式
public class AbstractDemo {
public static void main(String[] args) {
IFood food = Factory.getInstance("bread");
food.eat();
}
}
interface IFood{
public void eat();
}
class Bread implements IFood{
public void eat(){
System.out.println("吃面包");
}
}
class Milk implements IFood{
@Override
public void eat(){
System.out.println("喝牛奶");
}
}
// 调用时不需要考虑具体的接口子类,只需要输入指定的数据标记,就可以获得对应的接口对象
class Factory{
public static IFood getInstance(String className){
if (className.equals("bread")){
return new Bread();
} else if (className.equals("milk")){
return new Milk();
} else {
System.out.println("请输入正确的指标");
return null;
}
}
}
5. 代理设计模式
public class AbstractDemo {
public static void main(String[] args) {
IEat eat = new EatProxy(new EatReal());
eat.get();
}
}
interface IEat{
public void get();
}
class EatReal implements IEat{//设置代理类,客户端不必关心具体的实现
public void get(){
System.out.println("开始吃饭");
}
}
class EatProxy implements IEat{
private IEat eat;
public EatProxy(IEat eat){
this.eat = eat;
}
public void get(){
this.prepare();
this.make();
this.eat.get();
this.clear();
}
public void prepare(){
System.out.println("准备素材");
}
public void make(){
System.out.println("开始制作");
}
public void clear(){
System.out.println("洗碗扫锅");
}
}
6. 抽象类与接口的区别
No. | 区别 | 抽象类 | 接口 |
---|---|---|---|
1 | 关键字 | abstract class | interface |
2 | 组成 | 常量、变量、抽象方法、普通方法、构造方法 | 全局常量、抽象方法、普通方法、静态方法 |
3 | 权限 | 可以使用各种权限 | 只能是public,普通方法使用default |
4 | 关系 | 一个抽象类可以实现多个接口 | 子类不能继承抽象类,却可以继承多接口 |
5 | 使用 | 使用extends继承抽象类 | 子类使用implements实现接口 |
6 | 设计模式 | 模板设计模式 | 代理设计模式,工厂设计模式 |
7 | 局限 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
使用优先使用接口,避免使用抽象类
四、泛型
1. 泛型问题引出
public class AbstractDemo {
public static void main(String[] args) {
Point point = new Point();
point.setX(10);
point.setY(12);
int x = (Integer)point.getX();
int y = (Integer)point.getY();
System.out.println("x = "+x+"、y = "+y);
}
}
class Point{
private Object x;
private Object y;
public void setX(Object x){// 使用Object类接受各种类型的对象
this.x = x;
}
public void setY(Object y){
this.y = y;
}
public Object getX(){
return this.x;
}
public Object getY(){
return this.y;
}
}
2. 泛型基本定义
类中的属性或方法的参数与返回值的类型采用动态标记,在对象实例化的时候动态配置要使用的数据类型
public class AbstractDemo {
public static void main(String[] args) {
Point<Integer> point = new Point<>();// 使用各种引用数据代替泛型数据类型
point.setX(10);
point.setY(12);
int x = point.getX();
int y = point.getY();
System.out.println("x = "+x+"、y = "+y);
}
}
class Point<T>{// 泛型标记
private T x;
private T y;
public void setX(T x){
this.x = x;
}
public void setY(T y){
this.y = y;
}
public T getX(){
return this.x;
}
public T getY(){
return this.y;
}
}
3. 泛型通配符
为了可以适应所有本类的实例化对象,可以使用?作为泛型通配符使用,利用?表示的泛型类型只允许从对象中获取数据,不允许修改数据
public class AbstractDemo {
public static void main(String[] args) {
Message<String> msg = new Message<>();
msg.setContent("孙笑川");
fun(msg);
}
public static void fun(Message<?> temp){
System.out.println(temp.getContent());
}
}
class Message<T>{
private T content;
public void setContent(T content){
this.content = content;
}
public T getContent(){
return this.content;
}
}
如果使用设置泛型,是否可以实现上述功能?
虽然可以接收各种不同的实例化对象,但是也可以随意修改数据,而?通配符只允许获取,不允许修改
形式1:public static void fun(Message<Object> temp){}
形式2:public static void fun(Message temp){}
虽然Object是String的父类,可以接收一切的数据类型,但是在泛型中Message<String>
和Message<Object>
属于两个不同类型的对象
如果使用形式1的方式定义参数,则表示fun只能接收Message<Object>
类型的引用
如果使用形式2的方式定义参数,可以解决不同泛型类型对象的传递问题,但也使得数据可以随意被修改
通配符“?”可以匹配任意类型的泛型类型外,也可以通过泛型上限和反省下限的配置实现更严格的类范围定义
【类和方法】可以使用(? extends 类)
设置泛型上限:只能够使用类或当前类的子类设置泛型类型
?extends Number:可以设置Number或Number子类(例如,Integer类,Double类)
【方法】设置泛型下限(? super 类)
:只能够设置指定的类或指定类的父类
?super String:只能够设置String或者String的父类Object
1. 设置泛型上限
public class AbstractDemo {
public static void main(String[] args) {
Message<Integer> msg = new Message<>();
msg.setContent(10);
fun(msg);
}
// 设置了泛型的上限,实例化的对象Message只能使用Number和其子类作为泛型类型
public static void fun(Message<? extends Number> temp){
System.out.println(temp.getContent());
}
}
class Message<T>{
private T content;
public void setContent(T content){
this.content = content;
}
public T getContent(){
return this.content;
}
}
2. 设置泛型下限
public class AbstractDemo {
public static void main(String[] args) {
Message<String> msg = new Message<>();
msg.setContent("孙笑川");
fun(msg);
}
// 设置了泛型的下限,实例化的对象Message只能使用String和其父类Object作为泛型类型
public static void fun(Message<? super String> temp){
System.out.println(temp.getContent());
}
}
class Message<T>{
private T content;
public void setContent(T content){
this.content = content;
}
public T getContent(){
return this.content;
}
}
4. 泛型接口
定义泛型接口
interface IMessage<T> {
public String echo(T msg);
}
定义泛型接口子类,在子类中继续声明泛型
public class AbstractDemo {
public static void main(String[] args) {
IMessage<String> msg = new IMessageImpl<>();
msg.echo("孙笑川");
}
}
interface IMessage<T>{
public void echo(T msg);
}
class IMessageImpl<S> implements IMessage<S>{
public void echo(S t){
System.out.println("信息打印: "+ t);
}
}
定义子类,在子类中为IMessage设置泛型类型
public class AbstractDemo {
public static void main(String[] args) {
IMessage<String> msg = new IMessageImpl();
msg.echo("孙笑川");
}
}
interface IMessage<T>{
public void echo(T msg);
}
class IMessageImpl implements IMessage<String>{// 子类直接明确设置泛型类型
public void echo(String t){
System.out.println("信息打印: "+ t);
}
}
5. 泛型方法
public class AbstractDemo {
public static void main(String[] args) {
Integer num[] = fun(1,2,3);
for (int i : num){
System.out.println(i + "、");
}
}
public static <T> T[] fun(T... args){
return args;
}
}
此时是在一个没有泛型声明的类中定义了泛型方法,所以在fun()方法声明处就必须单独定义泛型标记,此时的泛型类型由传入的参数类型决定
总结
- java可以创建抽象类,专门当做父类。作用相当于模板,依据其格式来修改并创建新的类
- 抽象类包括普通方法和抽象方法,抽象方法没有方法体,要交给继承的子类进行强制性覆写
- 抽象类不能用new实例化对象,必须通过对象的多态性利用子类对象的向上转型进行实例化操作
- 接口是方法和全局常量的集合,接口必须被子类实现,子类单继承,接口多继承,接口通过多个extends继承多个接口
- 接口中允许default定义普通方法以及static定义的静态方法
- 接口也可以派生接口,或者称为子接口,不但保留父接口成员方法,还能添加自己的接口方法
- 使用泛型可以避免使用Object接受参数所带来的ClassCastException问题
- 泛型对象可以进行引用类型时一定要使用通配符?(或相关上限,下限设置)来描述泛型参数
习题
// 工厂代理模式,实现圆形正方形的抽象类设计
public class AbstractDemo {
public static void main(String[] args) {
Shape circle = Factory.getInstance("circle",2.4);
System.out.println(circle.area());
System.out.println(circle.perimeter());
Shape perimeter = Factory.getInstance("正方形", 2.4,24.3);
System.out.println(perimeter.area());
System.out.println(perimeter.perimeter());
}
}
abstract class Shape{
public abstract double area();
public abstract double perimeter();
}
class Circle extends Shape{
private double r;
public Circle(double r){
this.r = r;
}
@Override
public double area() {
return 3.14*Math.pow(this.r, this.r);
}
@Override
public double perimeter() {
return 2*3.14*this.r;
}
}
class Rectangle extends Shape{
private double x;
private double y;
public Rectangle(double x, double y){
this.x = x;
this.y = y;
}
public double area(){
return this.x*this.y;
}
public double perimeter(){
return 2*(this.x+this.y);
}
}
class Factory{
public static Shape getInstance(String className, double... args){
if (className.equalsIgnoreCase("circle")){
return new Circle(args[0]);
} else if(className.equalsIgnoreCase("正方形")){
return new Rectangle(args[0],args[1]);
}
else {
return null;
}
}
}