Java笔记
抽象类
使用场景
当父类方法不确定的时候,需要声明但是不知道该如何实现的时候,可以将其声明为抽象方法,这个类就是抽象类
1 2 3 4 5 6 7 8 9 10 11
| class Animal { private String name; public Animal(String name) { this.name = name; } public void eat() { System.out.println("吃一个..."); } }
|
这个时候可以将该方法设计为抽象方法,所谓抽象方法就是没有实现的方法(没有方法体)
1 2 3 4 5 6 7 8 9
| abstract class Animal { private String name;
public Animal(String name) { this.name = name; }
public abstract void eat(); }
|
当将一个类中的方法设置为抽象方法的时候,要将类也声明为抽象类
使用方法
1
| 访问修饰符 abstract 返回类型 方法名();
|
再用子类继承实现这些抽象方法
1 2 3 4 5 6 7 8 9
| class Dog extends Animal { public Dog(String name) { super(name); }
public void eat() { System.out.println("Dog eat"); } }
|
抽象类的价值更多的是在于设计,在设计者设计好后,让子类继承并实现
抽象类细节
1.抽象类不能够被实例化
2.抽象类不一定要包含抽象方法
3.抽象方法所在的类必须用abstract修饰
4.abstract只能修饰类和方法
5.抽象类可以有任意成员,非抽象方法,构造器,静态属性
6.抽象方法不能有主体
7.如果一个类继承了抽象类,则这个类必须要实现所有的抽象方法,除非他自己也声明为abstract类
8.抽象方法不能够用private final static修饰,因为和重写实现型成悖论
模板设计模式
抽象类的实践
需求
1.有多个类,完成不同的任务
2.要求能够统计得到各自完成的时间
3.编程实现
先使用不同的类实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package com.npu.abstract_;
public class Abstract { public static void main(String[] args) { new AA().job(); new BB().job(); } }
class AA { public void job() { long start = System.currentTimeMillis(); long num = 0; for (long i = 0; i < 800000; i++) { num += i; } long end = System.currentTimeMillis(); System.out.println(num); System.out.println(end - start); } }
class BB { public void job() { long start = System.currentTimeMillis(); long num = 0; for (long i = 0; i < 800000; i++) { num += i / 2; } long end = System.currentTimeMillis(); System.out.println(num); System.out.println(end - start); } }
|
可以将公共的部分提取出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.npu.abstract_;
public class Abstract { public static void main(String[] args) { new AA().calulateTime(); new BB().calulateTime(); } }
class AA { public void calulateTime() { long start = System.currentTimeMillis(); job(); long end = System.currentTimeMillis(); System.out.println((end - start)); }
public void job() { long num = 0; for (long i = 0; i < 800000; i++) { num += i; } long end = System.currentTimeMillis(); System.out.println(num); } }
class BB { public void calulateTime() { long start = System.currentTimeMillis(); job(); long end = System.currentTimeMillis(); System.out.println((end - start)); }
public void job() { long num = 0; for (long i = 0; i < 800000; i++) { num += i / 2; } long end = System.currentTimeMillis(); System.out.println(num); } }
|
如果说加上了C在C类中还是必须要实现calulateTime方法,所以可以将job写为一个抽象方法,calulateTime在抽象类里实现,用各个子类继承实现job即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package com.npu.abstract_;
public class Abstract { public static void main(String[] args) { new AA().calulateTime(); new BB().calulateTime(); } }
abstract class DD { public void calulateTime() { long start = System.currentTimeMillis(); job(); long end = System.currentTimeMillis(); System.out.println((end - start)); }
public abstract void job(); }
class AA extends DD { public void job() { long num = 0; for (long i = 0; i < 800000; i++) { num += i; } long end = System.currentTimeMillis(); System.out.println(num); } }
class BB extends DD { public void job() { long num = 0; for (long i = 0; i < 800000; i++) { num += i / 2; } long end = System.currentTimeMillis(); System.out.println(num); } }
|
在这里运用到了动态绑定机制,job调用的是子类的具体实现
运用到模板设计模式,也已大幅度提高代码的复用性,简易性
接口
快速入门
现实中使用充电器,usb接口,都有着统一的尺寸规定,方便用户使用,这种思路在语言编程中也有对应使用
可以用以下代码模拟
有如下接口
1 2 3 4 5
| public interface UsbInterface { public void start(); public void stop(); }
|
手机实现了接口
1 2 3 4 5 6 7 8 9 10
| package com.npu.interface_;
public class Phone implements UsbInterface{ public void start() { System.out.println("Phone start"); } public void stop() { System.out.println("Phone stop"); } }
|
相机实现了接口
1 2 3 4 5 6 7 8 9 10
| package com.npu.interface_;
public class Camera implements UsbInterface{ public void start(){ System.out.println("Camera start"); } public void stop(){ System.out.println("Camera stop"); } }
|
电脑使用接口
1 2 3 4 5 6 7 8 9
| package com.npu.interface_;
public class Computer { public void work(UsbInterface usbInterface) { usbInterface.start(); usbInterface.stop(); } }
|
就可以传入接口,使用接口实现的类的接口实现方法
1 2 3 4 5 6 7 8 9 10
| package com.npu.interface_;
public class Interface { public static void main(String[] args) { Camera camera = new Camera(); Phone phone = new Phone(); Computer computer = new Computer(); computer.work(phone); } }
|
代码中可以通过computer的work传入phone,调用phone的方法
基本介绍
接口就是给出一些没有实现的方法,封装在一起,在某个类要使用的时候,再根据具体的情况把这些方法写出来,语法如下:
1 2 3 4 5 6 7 8 9 10
| interface 接口名 { }
class 类名 implements 接口名 { }
|
注意:在jdk7之前的接口中没有方法体,都是抽象方法,而在jdk8之后接口可以有静态方法,默认方法,也就是接口中可以有方法的具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.npu.interface_;
public interface AInterface { public int num = 0; public void hi(); public default void lo() { System.out.println(1); } public static void test() { System.out.println("test"); } }
class A implements AInterface { @Override public void hi() { System.out.println("hi"); } }
|
接口的使用场景
1.在设计飞机时,专家规定飞机需要实现的功能/规格即可,让别人具体实现
2.项目经理,管理三个程序员,功能开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现
例如:3个程序员编写三各类,分别完成对于MySQL,Oracle,DB2数据库的 connect & close的定义,可以分别对三个数据库规定三个接口,三个程序员就必须要统一连接和关闭的方法名,接口也可以识别connect & close对应的是哪一个接口
接口的使用细节
1.接口不能够被实例化
2.接口中所有的方法是public方法,接口中的抽象方法可以不用abstract修饰方法也可以不用public修饰
3.一个普通类实现接口,就必须将接口中的方法都实现
4.抽象类实现接口,可以不用实现接口的方法
5.一个类可以同时实现多个接口(类的继承不允许继承多个类)
1 2 3 4
| class A implements IB, IC { }
|
6.接口中的属性只能是final的,而且是public static final 修饰符,比如
7.接口中属性的访问形式:
8.一个接口不能继承其他的类,但是可以继承多个别的接口
1
| interface A extends B, C {}
|
9.接口的修饰符只能够是public和默认,和类的修饰符同理
一个课堂练习,以下三种方式均可以成功访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.npu.interface_;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
public class AInterface { public static void main(String[] args) { A a = new A(); System.out.println(a.num); System.out.println(A.num); System.out.println(InterfaceTest.num); } } interface InterfaceTest { int num = 1; }
class A implements InterfaceTest {
}
|
接口VS继承
接口在Java中算单继承的补充,例如一只小猴,对于猴类来说,猴子天生就会爬树,这算是继承,但是如果要学会游泳,这只猴子就需要去学习游泳(实现游泳接口的功能)
接口比继承更加灵活,继承是is — a 的关系,而接口只需要满足 like — a 的关系
接口在一定程度上实现代码的解耦(接口的规范性 + 动态绑定机制)在后续讲解源码的时候会有所涉及
接口的多态特性
1.多态参数
例如在前面涉及到的入门,电脑在调用接口参数的时候,这个参数可以接受实现了这个接口的对象实例
1 2 3 4 5 6 7 8 9 10 11
| package com.npu.interface_;
public class Interface { public static void main(String[] args) { Camera camera = new Camera(); Phone phone = new Phone(); Computer computer = new Computer(); computer.work(phone); computer.work(camera); } }
|
另外一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.npu.interface_;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
public class AInterface { public static void main(String[] args) { InterfaceTest iface = new A(); InterfaceTest iface2 = new B(); } } interface InterfaceTest {} class A implements InterfaceTest {} class B implements InterfaceTest {}
|
接口类型应用变量可以指向实现了接口的对象实例,这个和继承多态类似(向下转型)
2.多态数组
同继承的多态,我们可以使用向下转型,使用运行类型的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.npu.interface_;
public class AInterface { public static void main(String[] args) { USBInterface[] usbInterfaces = new USBInterface[3]; usbInterfaces[0] = new PhoneA(); usbInterfaces[1] = new PhoneB(); usbInterfaces[2] = new PhoneA(); for (int i = 0; i < usbInterfaces.length; i++) { usbInterfaces[i].start(); if (usbInterfaces[i] instanceof PhoneA) { ((PhoneA) usbInterfaces[i]).call(); } } } } interface USBInterface { void start(); }
class PhoneA implements USBInterface { public void start() { System.out.println("Phone A start"); }
public void call() { System.out.println("Phone A call"); } }
class PhoneB implements USBInterface { public void start() { System.out.println("Phone B start"); } }
|
3.接口的多态传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.npu.interfacearr;
public class InterfacePoly { IG ig = new Teacher(); IH ih = new Teacher(); }
interface IH { void teach(); } interface IG extends IH { }
class Teacher implements IG { public void teach() { System.out.println("Teacher"); } }
|
内部类
一个类的内部内嵌套了另外的一个类,内部的类称作内部类,是类的五大成员之一:属性,方法, 构造器,代码块,内部类
1 2 3 4 5 6 7 8 9
| class A { class Inner { } }
class B { }
|
内部类的最大特点是可以直接访问私有属性,并且可以体现出类与类之间的包含关系,底层源码有大量的内部类
有四种内部类
第一类:定义在外部类局部上(比如方法内)
1.局部内部类
2.匿名内部类
第二类:定义在外部类的成员位置上
3.成员内部类(没用static修饰)
4.静态内部类(使用static修饰)
局部内部类
1.可以直接访问外部类的所有成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Outer { private int n1 = 100; public Outer(int n1) { this.n1 = n1; }
{ System.out.println("代码块"); }
public void teach() { System.out.println(n1 + 1); }
public void Print() { class Inner { public void function() { System.out.println(n1); teach(); } } System.out.println(n1); } }
|
2.不能添加访问修饰符,因为它的地位是一个局部变量,但是可以使用final修饰,内部类可以被类继承
3.作用域仅限于定义它的方法或代码块中
4.局部内部类访问外部类成员方式:直接访问
5外部类成员访问内部类方式:创建对象,再进行访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.npu.inner;
public class Main { public static void main(String[] args) { Outer outer = new Outer(1); outer.Print(); } }
class Outer { private int n1 = 100; public Outer(int n1) { this.n1 = n1; }
{ System.out.println("代码块"); }
public void teach() { System.out.println(n1 + 1); }
public void Print() { class Inner { public void function() { System.out.println(n1); teach(); } } System.out.println(n1); Inner inner = new Inner(); inner.function(); } }
|
6.外部其他类不可以访问内部类
7.如果外部类和内部类的成员重名的时候,默认遵循就近原则,如果想访问外部类的成员可以使用 外部类名.this.成员 访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.npu.inner;
public class Main { public static void main(String[] args) { Outer outer = new Outer(); outer.Print(); } }
class Outer { private int n1 = 100; { System.out.println("代码块"); }
public void Print() { class Inner { private int n1 = 200; public void function() { System.out.println(n1); System.out.println(Outer.this.n1); } } Inner inner = new Inner(); inner.function(); } }
|
匿名内部类(重要!!)
本质是一个类,是一个内部类,是匿名的,同时还是一个对象
个人理解:
匿名内部类就是一个渣男,使用的时候创建了这样的一个内部类,这个内部类就会跑路,再也没有办法使用,还留下了一个孩子(匿名内部类的实例)。当然,孩子是有用的,可以调用匿名内部类实现的一切方法,通过这样就可以不用额外的创建一个新的类,极大的简化了代码
基本语法
1 2 3
| new 类或接口 (参数列表) { 类体 };
|
代码示例:传统上而言 写一个类实现接口,创建对象,在主方法中创建类的对象完成接口创建Tiger实例与调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.npu.inner;
public class Anonymous { public static void main(String[] args) { Outer04 outer04 = new Outer04(); outer04.method(); } }
class Outer04 { private int n1 = 10; public void method() { IA tiger = new Tiger(); tiger.cry(); } }
interface IA { void cry(); }
class Tiger implements IA{ public void cry() { System.out.println("Tiger 叫"); } }
|
使用匿名内部类实现接口
不想创建类,只想单独一次实现接口,并使用方法,可以使用匿名内部类简化(这个匿名内部类不能被使用第二次)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.npu.inner;
public class Anonymous { public static void main(String[] args) { Outer04 outer04 = new Outer04(); outer04.method(); } }
class Outer04 { private int n1 = 10; public void method() { IA tiger = new IA() { public void cry() { System.out.println("tiger 叫"); } }; tiger.cry(); } }
interface IA { void cry(); }
|
在这个例子中,tiger的编译类型是IA接口类型,而tiger的运行类型则是由底层系统分配名称的匿名内部类,这个类实现了接口,并立即创建实例,可以通过实例访问接口方法
可以用tiger.getClass() 验证
运行结构
1 2
| tiger 叫 运行类型为:class com.npu.inner.Outer04$1
|
使用匿名内部类重写一个类方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.npu.inner;
public class Anonymous { public static void main(String[] args) { Outer04 outer04 = new Outer04(); outer04.method(); } }
class Outer04 { private int n1 = 10; public void method() { Father father = new Father("jack"){ int age = 19; @Override public void Print() { System.out.println(this.name+ " 年龄是 " + age); } }; father.Print(); System.out.println("运行类型为:" + father.getClass()); } }
class Father { String name; Father(String name) { this.name = name; }
public void Print() { System.out.println(this.name); } }
|
基于抽象类的匿名内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.npu.inner;
public class Anonymous { public static void main(String[] args) { Outer04 outer04 = new Outer04(); outer04.method(); } }
class Outer04 { private int n1 = 10; public void method() { Animal animal = new Animal(){ public void eat(){ System.out.println("吃"); } }; animal.eat(); System.out.println("运行类型为:" + animal.getClass()); } }
abstract class Animal { abstract void eat(); }
|
匿名内部类的使用细节
1.内部类创建完的结果是一个实例对象
我们要访问匿名内部类的方法可以通过这个实例对象直接用.调用方法,或者可以先用一个接口,抽象类,类的引用接收,再通过这个引用调用运行类型为匿名内部类的类方法
2.可以直接访问外部类的所有成员,包含私有的
3.不能添加访问修饰符,因为是一个局部变量
4.作用域位于处于的方法或者代码块
5.如果外部类和匿名内部类成员重名的时候,就近访问内部类的成员,要使用外部类的同名成员的时候需要使用 外部类名.this.成员名
外部类名.this指的是调用method()的对象
匿名内部类使用场景
1.当作实参直接传递,简洁高效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.npu.inner;
public class Test { public static void main(String[] args) { f1(new IL(){ @Override public void show() { System.out.println("鸡哥"); } }); } public static void f1(IL il){ il.show(); } }
interface IL{ void show(); }
|
传统上需要先实现一个实现了接口的类,再将类实例化,将实例传入f1,根据运行类型调用方法
一个代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.npu.inner;
public class Test { public static void main(String[] args) { Cellphone cellphone = new Cellphone();
cellphone.alarmclock(new Bell() { @Override public void ring() { System.out.println("ring1"); } });
cellphone.alarmclock(new Bell() { @Override public void ring() { System.out.println("ring2"); } }); } }
interface Bell { void ring(); }
class Cellphone { public void alarmclock (Bell bell) { bell.ring(); } }
|
成员内部类
成员内部类定义在外部类的成员位置
1.内部类可以访问外部类的所有成员,包含私有的
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.npu.inner;
public class Member { public static void main(String[] args) { Outer08 outer = new Outer08(); outer.inner.Print(); } }
class Outer08 { private int n1 = 200; Inner inner = new Inner(); { System.out.println("代码块"); } class Inner { public void Print() { System.out.println(n1); } } }
|
2.成员内部类可以用访问修饰符修饰,因为它的地位是类的成员
3.作用域为整个类体中
4.访问同名成员的时候会遵循就近原则,要访问外部类的成员要使用 外部类名.this.重名成员 来进行访问
5.外部其他类可以有如下的两种方式访问成员内部类
1 使用外部类对象 通过new来创建一个成员内部类对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.npu.inner;
public class Member { public static void main(String[] args) { Outer08 outer = new Outer08(); Outer08.Inner inner = outer.new Inner(); } }
class Outer08 { private int n1 = 200;
class Inner { public void Print() { System.out.println(n1); } } }
|
2 再外部类中编写一个方法返回一个Inner的实例(将第一种写为方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.npu.inner;
public class Member { public static void main(String[] args) { Outer08 outer = new Outer08(); Outer08.Inner inner = outer.getInner();
} }
class Outer08 { private int n1 = 200;
class Inner { public void Print() { System.out.println(n1); } }
public Inner getInner() { return new Inner(); } }
|
静态内部类
定义在外部类的成员位置,由static修饰
1.可以直接访问外部类的所有静态成员,包含私有的,但是不可以访问非静态成员
2.成员内部类可以用访问修饰符修饰,因为它的地位是类的成员
3.作用域为整个类体中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.npu.inner;
public class Member { public static void main(String[] args) { Outer08 outer = new Outer08(); outer.getInner();
} }
class Outer08 { private int n1 = 200; private static int n2 = 100;
public static class Inner { public void Print() { System.out.println(n2); } }
public void getInner() { Inner inner = new Inner(); inner.Print(); } }
|
4.外部其他类使用静态内部类有以下几种方式
1 用外部类静态创建内部类(不会创建外部类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.npu.inner;
public class Member { public static void main(String[] args) { Outer08.Inner inner = new Outer08.Inner(); inner.Print(); } }
class Outer08 { private int n1 = 200; private static int n2 = 100;
public static class Inner { public void Print() { System.out.println(n2); } }
}
|
2 静态方法返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.npu.inner;
public class Member { public static void main(String[] args) { Outer08.Inner inner = Outer08.getInner(); inner.Print(); } }
class Outer08 { private int n1 = 200; private static int n2 = 100;
public static class Inner { public void Print() { System.out.println(n2); } }
public static Inner getInner() { return new Inner(); } }
|
5.在遇到同名成员的时候默认就近访问,要访问外部类成员需要使用 外部类名.成员访问