第三章 面向对象(补) : API 、 接口、抽象类
第一节 API
3.1.1 API
API : 应用程序开发接口,即由sun 公司封装好的类。
3.1.2 Object 类
Object 在 java.lang 包中提供的类,不需要 import 导包。
特点:所有类都是它的子类。(接口不是类,没有构造方法)Object有空参构造方法。
注 : Java中的继承是单继承,但是具有传递性。 所以,Object 是一切类的父类。
常用内部方法:
-
equals():两个对象做比较方法,默认比较内存地址。
注:equals() 与 == 的区别?
在引用数据类型中,== 严格用于比较对象的内存地址,返回一个布尔值。只要 new 出的对象一定是不同的内存地址,数据一样也没用,所以比较地址很多时候与实际不符。例如想判断两个 String 的内容是否相等,就需要 调用 equals 方法。 (复习:由于多态特性, 子类重写的方法会覆盖父类的方法) -
toString():toString 默认返回的是对象的内存地址。
输出语句中,println(对象),其实就是调用对象的 toString 方法。 -
clone(): 完成对象的浅拷贝,返回对象的引用。
注: 浅拷贝是只拷贝对象的引用(地址) ,深拷贝是重写开辟一块内存空间,并复制对象。 -
hashCode() : 返回对象的 hashCode。
如果不使用 HashMap 之类的集合框架, 可以说 hashCode 没有什么用。 hash
code 默认返回对象地址,我们知道不同对象内存中的地址是不一样的,当需要根据内容判断两个对象相同且 hash 到同一位置时,就需要自己指定规则重写 hashcode。 -
notify() : 在同步代码块中,当对象作为锁的时候唤醒一个线程。
-
wait() : 在同步代码块中使用, 阻塞线程等待其他线程唤醒。
3.1.3 抽象类
抽象类:含有抽象方法的类叫抽象类。(没有抽象方法没有必要写成抽象类)
抽象类功能:抽象类除了不能实例化,与普通类无异,这导致抽象类只能被继承。如果抽象类被继承,那么它的子类必须重写抽象方法。
抽象方法格式:利用 abstract 关键字,如果不写{},叫做没有主体的方法。
抽象方法定义: public abstract void ***();
抽象类设计思想:继承体系,强制子类重写抽象方法,要求程序员思考程序的完整性。在程序设计之初一般布局好抽象类。起到一种对子类的约束
注意:
- 抽象方法必须存在于抽象类中
- 子类继承抽象类,只有重写了抽象类中的所有抽象方法,才可以实例化,创建对象。否则子类必须还是一个抽象类!
- 抽象类不能和 private、final、static 一起使用。
第二节 接口
3.2.1 接口
-
接口:接口是一个标准,地位等价于类, 起到对接口实现类的方法约束作用。接口比抽象类更抽象,只有抽象方法没有具体实现(抽象类可以拥有方法的实现)
-
目的:接口实现功能定义与显示分离,优化程序设计。一切事物都有功能存在,均有接口。接口反映一种can-do关系,而抽象类反映的是is-a关系。
-
定义:使用interface关键字 interface 与 class 不同,只能定义抽象方法,但有固定格式:
public 返回值 方法名(参数列表); -
接口中只能声明的常量:
public static final 数据类型 变量名 = 值; -
接口中的抽象方法:接口中方法只能是公共抽象方法,即使不写 public 也是 public 权限,而不是 default 权限。接口无法定义普通的成员变量,只能定义常量。也不能有构造函数,全部都是抽象的。
-
如果看到接口的引用,那一定是引用了接口的实现类。(多态调用)
如果方法的参数是接口,那传递的也是接口的实现类。
3.2.2 接口的实现
-
接口的实现:类实现接口(类似继承),使用关键字 implements 。 类实现接口,需要重写接口中的抽象方法。
-
格式:
class 类名 implements 接口{}称为接口实现类。实现类需要重写接口中的全部抽象方法,才可以建立对象。 -
接口的多实现:一个类可以多实现多个接口,而继承,只能单继承。接口解决了安全性问题。例如public class c implements A,B{} //c要重写a、b的全部方法,public void XXX(){}。
继承类的同时实现多接口:public class c extends B implements E,F {} //最终的任务都压向了底层类,底层需要全部重写顶层方法。
注意:
- 接口与接口之间关系:继承关系!!并且接口之间可以多继承。
- 面试题: java中有多继承吗?答:类没有多继承,接口中有多继承。
- 接口中甚至可以没有任何方法,就是一个标准。
- 适配器类:避免每次实现类需要重写接口的全部抽象方法,中间可以加一个过度,子类再去继承适配器类。
第三节 内部类
内部类:在一个类的内部定义类。可以定义在方法里面(局部位置),也可以定义在成员变量位置。
内部类的编译:内部类编译完也是.class 文件,只要是类就会产生.class文件。
调用规则:可以使用外部成员以及私有成员,而外部类要使用内部类成员,得new对象。例如:
Outer.Inner p = new Outer().new Inner();
p.func();
3.3.1 成员内部类
等价于一个普通的成员。访问权限私有、静态啊,都可以设置。
但与成员的区别是,可以实现接口、继承……即具有类的属性。
例如:
public class Outer(){
private static class Inner(){
public void func(){}
}
..........get
..........set
}
- 调用比较复杂。
- 最简单的调用是,在 Outer 类的 方法中调用成员内部类,相当于访问了类的一个属性。
- 成员内部类中调用外部类的当前对象: 类名.this
- 静态成员内部类:static class Inner{} 类似与 一个静态成员。静态 Inner 中只能访问 Outer 的静态成员。
3.3.2 局部内部类:
class {
func1{
class{
func2{}
}
}
}
这种结构特别难受。想调用 func2 ,在 func 里 new 一个对象。
3.3.3 匿名内部类:
实现一个接口,或者继承一个类。
new 接口或者父类(){
// 重写抽象方法
func(){}
};
整个公式得到了一个匿名对象, 可以调用方法:
new 接口或者父类(){
// 重写抽象方法
func(){}
}.func();
2021.2.3 补充 :
匿名内部类在传接口时比较常用, 例如 : 测试多线程
public class Test {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}, "线程" + i).start();
}
}
}
注:补充内容:代码块
- 局部代码块:{代码},就是限制变量的作用域(不常用)。
- 构造代码块:在 class 中加入一个{},new对象时直接执行。每 new 一次执行一次,优先于构造方法。
- 静态代码块:在 class 中加入 static{},类加载时执行,且只执行一次。
其他程序设计问题:
- 程序设计时,应该避免臃肿,即类的功能不单一。实现一个相关功能,最好单建立一个类。例如,要处理容器问题,不要在其他类中处理,单建立一个类处理与容器相关的所有操作。
- 如果你提供了一个聚集,那么你应该提供遍历某种元素和查找某种元素的方式。这个就是迭代器思想。(聚集: 放东西的容器)
- 迭代器:Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法。
- 函数的功能也应该单一化。程序设计的时候,完全可以写多个方法将函数的功能拆解。
- 代码应该具有可阅读性,方便二次维护。
- 重点:面向对象中,谁拥有数据,谁拥有行为! 如果这个行为的所有数据全部来源于别的类,这就破坏了可封装性!
例如:人在黑板上画圆,这个“画圆”的行为应该给圆类。虽然我们主观上看人是执行者,但应该是人调用圆的“被画”方法。 - 代码要具有可扩展性,对于原来的代码尽量少修改。修改原来的代码意味着重新编译,重新发布,这样增加了项目成本。












网友评论