文章目录
面向对象高级知识(二)
final关键字
类
父类使用final声明则不存在子类,不可被继承
一般编写应用开发不需要final,只有进行系统架构代码开发才会使用
String是用final声明,故String不能被继承
方法
父类中声明位final的方法不能被子类覆写
常量
声明时一定要记得赋值
使用final声明的变量就会变成常量,不可更改
java中常量的命名要全部大写,以此与变量区分开来
全局常量
final static 类型 常量名 = ****;
多态性
方法的多态性: 重载和覆写
对象的多态性: 父子类对象的转换
向上转型: 子->父----------父类对象=子类实例, 自动转换
向下转型: 父->子----------子类对象=(子类)父类对象, 强制转型
向上转型无条件,向下转型只有在发生过向上转型之后才能向下转型
不要看类的名称,只看实例化对象的类,到底属于那个类
如果是两个没有关系的类发生强制类型转换,就会出现ClassCastException异常
向下转型存在隐患,应当避免
对象多态性的作用
对象多态性本质是根据实例化对象所在的类是否覆盖了父类中的指定方法来决定最终执行的方法体
class A{}
class B extends A{}
public class Test{
public static void main(String[] args){
A a = new B(); //向上转型
B b = (B) a; //向下转型
}
}
向上转型
比如在之前链表操作里,向上转型可以统一参数类型
在写方法的时候只要接受父类类型的参数,子类类型的参数都可以被接受
向上转型是统一调用的参数类型
向下转型
执行子类个性化操作
发生继承关系后父类对象的使用方法必须在父类中明确定义
父类不知道派生子类的新方法,所以发生向上转型
有些特殊方法父类没有在父类中声明无法使用,这里使用向下转型进行个性化操作
当向下转型后,子类会继承父类的属性,然后还可以继续使用本类的特殊方法
简单类要以父类方法为主,少给子类加功能
80%情况使用向上转型
5%使用向下转型
15%不使用转型
使用instanceof关键字可以判断当前对象是否是指定类的实例
A a = new B();
a instanceof A; //true
a instanceof B; //true
//在进行向下转型的时候都要用instanceof进行判断
if (a instanceof B){
B b = (B) a;
}
抽象类
明确定义了子类需要覆写的地方,在语法上进行严格要求,代码开发会更加标准
抽象类在普通类中加入了抽象方法的组成部分
抽象方法是没有方法体的方法,
抽象方法只有方法名,没有方法体{}使用abstract关键字进行定义
拥有抽象方法的类就叫做抽象类
抽象类使用abstract class定义
抽象类只是比普通类多了抽象方法的定义,其他结构和普通类一样
抽象类不能直接实例化对象
- 抽象类必须拥有子类
- 抽象类的子类(不是抽象类)必须覆写抽象类中的全部抽象方法
- 依靠对下个向上转型的概念,子类完成抽象类实例化对象操作
而后在调用父类的抽象方法时,使用的是子类覆写的方法(转型后变成了子类)
tips
开发中继承一个普通类还是抽象类
普通类自愿覆写,抽象类强制覆写
抽象类更加严谨
虽然一个子类可以继承任意一个普通类
但是开发中尽量不要继承普通类而是继承抽象类
-
抽象类继承自类里面有明确的覆写要求普通类没有
-
抽象类只比普通类多了一些抽象方法的定义,组成方法和普通类一样
-
普通类对象可以直接实例化,但是抽象类必须经过向上转型后才可以得到实例化对象
抽象类的相关限制
-
抽象类可以存在属性,所以存在构造方法给属性赋值,子类对象实例化满足限制性父类构造再调用子类构造的情况
-
抽象类不能使用final
-
抽象类可以没有任何抽象方法,但是只要被声明成抽象类就不能直接使用new实例化对象
-
抽象类内部可以继续定义抽象类,实现的子类根据需要选择是否定义内部类来继承抽象内部类**(不是必须继承)**
-
外部抽象类不允许使用static,
但内部抽象类可以,就相当于一个外部抽象类
-
抽象类中定义了static方法或者属性就可以在没有实例化对象的情况下使用
隐藏抽象类的子类
在设计系统类库中经常使用在抽象类内部定义static子类继承父类
即子类被声明成父类的static内部类
可以定义一个获取本类实例化对象的方法getinstance()
途径static内部子类的向上转型得实例化对象,这个步骤在类内完成对用户透明
abstract class A{
public abstract void print(); //抽象方法
private static classB extends A{
public abstract void print(){
System.out.println(""); //覆写
}
}
public static A getinstance(){
return new B(); //向上转型,static声明方法可以直接调用
}
}
public class Test{
A a = A.getinstance(); //这里实例化的其实是B向上转型的对象
a.print;
}
属性初始化
在任何一个类的构造执行完成之前,所有属性的内容都是其对应数据类型的默认值
在构造完成之前,对象属性都是默认值
注意父类和子类构造过程中某些方法的调用顺序,不然用以引起bug
抽象类的应用——模板设计模式
抽象父类中的普通方法内部调用抽象方法时,调用的一定是子类覆写后的方法
至于执行的是哪个子类的覆写方法要看实例化是哪个子类进行了向上转型
在设计类的过程中应该先对行为进行抽象
根据每种行为创造具体的子类,子类的具体操作都应该有行为类发出命令(conmmand()普通方法,接受不同的参数已调用类中包含的抽象类,他声明在父类中)
根据需要对不同的抽象方法进行覆写
如果子类不需要某方法覆写为空即可
模板设计模式
接口
java接口解决了单继承的局限
基本定义(1.8版本以下最基础的定义)
如果一个类只有抽象方法和全局变量组成,要将其定义成接口而不是抽象类。
严格来讲是一个特殊类,其中只有全局变量和抽象方法
java中使用interface关键字来实现接口定义
interface A{
public static final String MSG = "WDNMD";
public abstract void print();
}
接口对象不能使用关键字new进行实例化操作,使用接口有以下原则:
-
接口必须有子类, 子类可以使用implements关键字实现多个接口
-
接口的子类(如果不是抽象类),必须覆写接口的所有抽象方法
-
接口的对象可以利用子类的向上转型操做进行实例化
接口的简化定义:抽象方法+全局变量
接口中只能够使用一种访问权限:public
声明内部抽象类和全局变量时可以不加abstract
-
实现多个接口
子类实现了多少个接口,每个接口的抽象类都要覆写
如果有多个实现的接口, 实质上实例化的还是同一个子类,具体要看子类向上转型的哪个接口
-
子类既要继承抽象类,又要实现接口
先继承抽象类(extends)再实现接口实现(implements)
interface A{}
interface B{}
abstract class C{}
class X extends C implements A,B{}
-
一个抽象类可以继承一个抽象类和实现多个接口
一个接口却不能继承抽象类
一个接口可以使用extends关键字同时继承多个父接口
interface A{}
interface B{}
interface C extends A,B{}
class X implements C{}
- 抽象类的限制比接口多,因此接口解决多继承问题
- 接口内部可以定义普通内部类, 抽象内部类, 内部接口
- 如果内部使用static定义一个内部接口,该接口相当于一个外部接口
内部接口在类集中学习,在安卓开发中会出现
接口的功能:
定义不同层之间的操作
表示以一种操作能力
将服务器端的远程方法试图暴露给客户端: 分布式开发
接口的实际应用: 标准
通过引用传递和接口,可以自由的调用不同类型的类作为参数
外部方法只要将参数类型设置为接口的类名就行
工厂设计模式(Factory)
java开发中使用最多的设计模式
- 客户端调用简单,不需要关注具体的细节
- 程序代码改动许映象客户端调用
客户端关注如何取得实现接口的对象
重点是关键字new 到底是哪个实现了接口的类在实例化对象
解决关键字new带来的耦合问题
参考JVM的原理,再加一个中间层
让客户端只看见接口而看不见子类
需要一个中间工具类获得接口对象
tips
Spring框架的设计核心理念就是解决代码耦合问题
解决办法就是增加一个工厂类进行过渡
class Factory
工厂类接受参数决定到底实例化哪个子类
在很多框架中都有工厂类通过工厂类方法Factory.getInstance()来获得实例而不是直接new一个对象出来
interface Fruit{}
class Apple implements Fruit{}
class Orange implements Fruit{}
class Factory{
public static Fruit getInstance(String className){
if("Apple".equals(className)){
return new Apple();
}else if("Orange".equals(className)){
return new orange();
}else
return null;
}
}
代理设计模式(Proxy)
代理设计就是指一个代理主题来操作真实主题
真实主题执行具体业务操作
代理主题负责其他相关业务处理
interface Network{
public browse();
}
class Real implements Netwrok{
public void browse(){}
}
class Proxy implements Network{
private Network network;
public Proxy(Network network){
this.network = network;
}
public void other(){}
public void browse(){
this.other();
this.network.browse();
}
}
public class Test(){
public static void main(String args[]){
Network net = new Proxy(new Real());
net.browse();
}
}
真实主题(real)只是完成了最基本的功能
代理主题(Proxy)则是完成了比真实主题更多的业务操作
相关: Spring的AOP设计理念
抽象类和接口使用建议:
- 在进行某些公共操作是一定要定义接口
- 有了接口就需要子类完善方法
- 如果自己写接口一定不要使用关键字new直接实例化接口子类,而是使用工厂类实现
接口是建立在类之上的标准
所有设计中接口是最先被设计出来
接口设计最为重要
接口设计案例
接口Pet
package PetShop;
public interface Pet {
/**
* 获取宠物姓名
* @return
*/
public String getName();
/**
* 获取宠物年龄
* @return
*/
public int getAge();
}
功能外部类
package PetShop;
import Object_Link.Link;
/**
* 本类中的链表定义使用了Object_Link的类
*/
public class PetShop {
private Link pets = new Link();
/**
* 给宠物商店上架宠物
* @param pet
*/
public void add(Pet pet){
pets.add(pet);
}
/**
* 宠物商店下架宠物
* @param pet
*/
public void remove(Pet pet){
pets.remove(pet);
}
/**
* 模糊查询宠物信息,String的contains方法
* @param keyword
* @return
*/
public Link search(String keyword){
Link result = new Link();
Object obj[] = this.pets.toArray(); //使用Object数组接收
for(int i=0;i<obj.length;i++){
Pet p = (Pet) obj[i]; //向下转型成Pet类
if(p.getName().contains(keyword)){
result.add(p);
}
}
return result;
}
}
狗子类
package PetShop;
public class Dog implements Pet{
private String name;
private int age;
private Dog(String name, int age){
this.name=name;
this.age=age;
}
@Override
public String getName() {
return this.name;
}
@Override
public int getAge() {
return this.age;
}
/**
* 判断是否相等
* @param obj
* @return
*/
public boolean equals(Object obj){
if(this==obj){
return true;
}
if(obj == null){
return false;
}
Dog c = (Dog) obj; //向下转型
if(obj instanceof Dog){
if(this.name.equals(c.name)&&this.age==c.age){
return true;
}
}
return false;
}
public String toString(){
return "狗的名字:"+this.name+"狗的年龄"+this.age;
}
}