We believe that writing is about content, about what you want to say – not about fancy formatting.
我们坚信写作写的是内容,所思所想,而不是花样格式。
— Ulysses
Java第5天上午
上午主要讲了抽象类和接口
抽象类:
问题提出:
利用多态性实现不合理:
抽象的概念定义为抽象类/抽象方法更加合理
注意上图中抽象方法的形式 没有{ }!
抽象类的特点:
- 抽象类不能直接实例化,但可以声明出对象的引用以指向非抽象子类的对象(格式统一 方便编程)
- 除非子类采用具体方法替代抽象类中的全部抽象方法,否则子类本身也被自动认为是抽象的!此时必须手动在子类上加上abstract关键字!!!
编程练习:编写程序,创建两个几何对象,一个圆和一个正方形,调用方法检查两个对象是否有相同的面积,
// Shape.java:
public abstract class Shape {
/**
* 抽象获取面积的方法
* @return 形状面积
*/
public abstract double getArea();
/**
* 比较两个形状的大小
* @param p 另一个形状的实例
* @return 如果当前形状较大则返回true
*/
public boolean compareArea(Shape p){
return this.getArea()>p.getArea();
}
}
// Circle.java:
public class Circle extends Shape{
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI*radius*radius;
}
}
// Square.java:
public class Square extends Shape{
private double length;
public Square(double length) {
this.length = length;
}
@Override
public double getArea() {
return length*length;
}
}
// Test.java:
public class Test {
public static void main(String[] args) {
Shape s1 = new Square(2);
Shape s2 = new Circle(2);
System.out.println(s1.getArea());
System.out.println(s2.getArea());
System.out.println(s1.compareArea(s2));
}
}
// 结果:
4.0
12.566370614359172
false
接口:
接口更抽象,仅声明了方法:
- 注意接口用 interface 关键字
implements关键字实现接口:
- 通过这样的定义,Professor类替代了Teacher接口的所有方法,因此Professor类是一个具体的类。
- 但是如果Professor类没有替换所有的接口,则Professor类只能看成是一个抽象的类。编译器会要求在Professor类的前面加上abstract关键字。
- 因此,实现接口实际上就是在创建抽象类的子类时,给抽象方法“加上具体的内容”。
接口和抽象类区别:
- 接口与抽象类的不同:
- 接口只抽象行为,而抽象类则要指定属性、具体的方法和抽象的方法。
- 工程中,接口用的比抽象类更多一些
- 抽象类只能单继承,而接口可以多实现!但是,当将继承(或实现)的子类有默认方法实现时,必须用抽象类(毕竟是个class,功能多一些)
Abstract class | Interface | |
---|---|---|
实例化 | 不能 | 不能 |
类 | 一种继承关系,一个类只能使用一次继承关系。可以通过继承多个接口实现多重继承 | 一个类可以实现多个interface |
数据成员 | 可有自己的 | 静态的不能被修改即必须是static final,一般不在此定义 |
方法 | 可以私有的,非abstract方法,必须实现 | 不可有私有的,默认是public,abstract 类型 |
变量 | 可有私有的,默认是friendly 型,其值可以在子类中重新定义,也可以重新赋值 | 不可有私有的,默认是public static final 型,且必须给其初值,实现类中不能重新定义,不能改变其值。 |
设计理念 | 表示的是“is-a”关系 | 表示的是“like-a”关系 |
实现 | 需要继承,要用extends | 要用implements |
一个类可以实现自多个接口:
类需要替代这两个接口所定义的所有方法
重点说明: (用接口实现多重继承)
多重继承发生问题原因之一在于属性(数据结构)冲突,也就是存储空间的冲突。由于接口不与任何存储空间相关联,因此可以解决存储空间冲突的问题
对于继承的方法的冲突,当使用接口之后,由于接口只定义了方法的抽象,没有具体的执行代码,因此也不会发生代码冲突的问题。
拓展示例:
- 非抽象类(具体类)不能包含抽象方法
- 如果抽象父类的子类,没有实现所有的抽象方法,它必须声明为抽象的(在这个类声明的前面加上abstract关键词) (貌似和前面重复了,但是很重要,再说一次也无妨)
- 在一个由抽象类扩展出来的非抽象类中,所有的抽象方法都必须实现,即使这个子类不使用它们。
- 抽象类不能用new运算符实例化,但仍应定义它的构造方法,这种构造方法将在它子类的构造方法中被调用。
- 允许声明没有抽象方法的抽象类。(用于定义新子类)
- 子类可以被声明是抽象的,即使它的父类是具体的。
- 子类可以覆盖它父类的方法,并将其声明为抽象的。(当父类方法中的实现在子类中无效时)
对比补充:
抽象类——模板设计模式:
抽象类小练习:
- 抽象类不能实例化,有构造方法;接口也不能实例化,但没有构造方法!
- 接口不是类,不能继承类,可以继承一个或多个接口
- 接口不能实现接口!!(只有类可以实现接口,注意区分实现和继承接口的区别!)
- 抽象类是类,既可以继承类,也可以实现很多其他接口
final:
拓展阅读:
1 - Java 接口的作用和好处
2 - Java 接口(interface)的用途和好处
3 - Java 抽象类与接口的区别
4 - 详细解析Java中抽象类和接口的区别
拓展:
了解Math类:
The class contains methods for performing basic numeric operations such as the elementary exponential, logarithm, square root, and trigonometric functions
里面包含了很多常用常量(如e和PI)和数学函数(如sin()、con()等):
下午讲了内部类:
Java内部类
在Java中,可以将一个类定义在另一个类里面或者一个方法(类成员函数)里面,这样的类称为内部类。广泛意义上的内部类一般来说包括四种:
- 成员内部类
- 局部内部类
- 匿名内部类
- 静态内部类
成员内部类:
- 最普通的内部类,它的定义位于另一个类的内部
class Circle {
double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { // 成员内部类
public void drawSahpe() {
System.out.println("drawshape");
}
}
}
看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)
class Circle {
private double radius = 0;
public static int count =1;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
System.out.println(count); //外部类的静态成员
}
}
}
要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
class Circle {
private double radius = 0;
public Circle(double radius) {
this.radius = radius;
getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问
}
private Draw getDrawInstance() {
return new Draw();
}
class Draw { //内部类
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
}
}
}
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:
public class Test {
public static void main(String[] args) {
//第一种方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建
//第二种方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() {
}
}
}
内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。比如上面的例子,如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。
局部内部类:
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
匿名内部类:
匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。
下面这段代码是一段Android事件监听代码:
scan_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
这段代码为按钮设置监听器,这段代码中的:
new OnClickListener() {
public void onClick(View v) {
}
}
上面就是匿名内部类的使用。代码中需要给按钮设置监听器对象,使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。
当然像下面这种写法也是可以的,跟上面使用匿名内部类达到效果相同:
private void setListener()
{
scan_bt.setOnClickListener(new Listener1());
history_bt.setOnClickListener(new Listener2());
}
class Listener1 implements View.OnClickListener{
@Override
public void onClick(View v) {
}
}
class Listener2 implements View.OnClickListener{
@Override
public void onClick(View v) {
}
}
这种写法虽然能达到一样的效果,但是既冗长又难以维护,所以一般使用匿名内部类的方法来编写事件监听代码。同样的,匿名内部类也是不能有访问修饰符和static修饰符的。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
静态内部类:
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
内部类的使用场景和好处:
为什么在Java中需要内部类?
总结一下主要有以下四点:
- 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整
- 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏
- 方便编写事件驱动程序
- 方便编写线程代码
认为第一点是最重要的原因之一,内部类的存在使得Java的多继承机制变得更加完善
补充:
关于成员内部类的继承问题。一般来说,内部类是很少用来作为继承用的。但是当用来继承的话,要注意两点:
- 成员内部类的引用方式必须为 Outter.Inner 形式
- 构造器中必须有指向外部类对象的引用,并通过这个引用调用super() (见下面代码)
class WithInner {
class Inner{
}
}
class InheritInner extends WithInner.Inner {
// InheritInner() 是不能通过编译的,一定要加上形参
InheritInner(WithInner wi) {
wi.super(); //必须有这句调用
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner obj = new InheritInner(wi);
}
}
每日一练:
课堂作业:
没留 暂无
课外练习:
课外作业点我查看
拓展阅读:
1 - Java内部类的使用小结
2 - java提高篇(八)----详解内部类
3 - java中的内部类总结
后记
世界上所有的追求都是因为热爱
一枚爱编码 爱生活 爱分享的IT信徒
— hongXkeX