一、代码块的练习
package com.alice.day.day513;
import com.use.Test;
/**
* 第一题
* 下面的代码输出什么?
*/
public class CodeBlockExercise01 {
// 第二题的内容
Sample sam1 = new Sample("sam1成员初始化"); // 该类静态完成之后普通
// 这里会先加载类的信息,所以sam1是普通成员属性,不是静态的不会被先加载
static Sample sam = new Sample("静态成员sam初始化"); // 是静态先加载
// 因为这里new Sample创建实例对象会加载类的信息,所以又去跑去加载类信息,然后普通,然后构造
static {
System.out.println("static块执行"); // 回到该类执行剩下的还没有完成的静态代码块
if (sam==null) System.out.println("sam is nul"); // 已经有地址不会执行,到此为止
// 静态完成,然后是普通
}
CodeBlockExercise01(){
System.out.println("CodeBlockExercise01默认构造函数被调用"); // 最后构造
}
public static void main(String[] args) {
// 1
// System.out.println("total = " + Person.total); // total =
// // 分析,这里Person.total调用的是类的东西,肯定要加载类的信息,而加载类的信息是按照
// // 先静态,优先级相同,谁在前面谁先执行,所以先total,然后静态代码块,而这里的total
// // 没有输出,所以这里total先被初始化为0然后被赋值为100,然后打印in static block!
// // 然后回到调用处Person.total 打印total = 100;
// // 即:
// // in static block!
// // total = 100
// System.out.println("total = " + Person.total);
// // 第二次因为类的信息只会加载一次,所以这里静态代码块就不执行了,直接打印:
// // total = 100
// 2、
CodeBlockExercise01 a = new CodeBlockExercise01();
// 其实主函数什么都不写还是会有输出的,因为Java帮我们调用了静态main,导致类信息被加载
// 这里创建对象调用无参构造方法
/*
输出:
Sample
你好
静态成员sam初始化
static块执行
你好
sam1成员初始化
CodeBlockExercise01默认构造函数被调用
*/
}
}
class Person {
public static int total;
static {
total = 100;
System.out.println("in static block!");
}
}
/**
* 第二题,下面的代码输出什么
*/
class Sample {
static {
System.out.println("Sample"); // 先被执行
// 第一个输出Sample
}
{ // 创建对象导致普通的代码块又被执行
System.out.println("你好"); // 然后普通
}
Sample(String s) { // 最后构造器
System.out.println(s); // 最后构造
}
Sample() {
System.out.println("Sample默认构造函数被调用");
}
}
二、单例设计模式(入门)
package com.alice.day.day513;
/*
单例设计模式
什么是设计模式
1、静态方法和属性的经典使用
2、设计模式是在大量的实践中总结和理论化之后优选的代码结构,编程风格,以及解决
问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们需要使用不同的棋谱
免得再次去思考和摸索。
这里考虑设计模式的时候会使用到大量和静态相关的内容。
什么是单例模式
1、所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,
并且该对象只能提供一个取得其对象实例的方法
2、单例模式有两种方式:
第一种是 饿汉式
第二种是 懒汉式
实现步骤:
1、构造器私有化,防止直接new一个对象,但是这样我们不能有女朋友
2、类的内部创建对象,可以有女朋友但保证只有一个,这个时候就需要使用到static静态属性
3、向外暴露一个静态的公共方法,getInstance给一个实例,让我们得到女朋友
4、代码实现
*/
public class SingletonDesignPattern02 {
public static void main(String[] args) {
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
// 获取我们可以一直获取,但是其实都是同一个对象
GirlFriend instance1 = GirlFriend.getInstance();
System.out.println(instance1);
System.out.println(instance == instance1);
}
}
// 有一个类,GirlFriend
// 只能有一个女朋友
// 像下面这种就不是一个单例设计模式
//GirlFriend xh = new GirlFriend("小红");
//GirlFriend xb = new GirlFriend("小白");
//class GirlFriend {
// private String name;
//
// public GirlFriend(String name) {
// this.name = name;
// }
//}
/**
* 为什么叫做饿汉式,
* 创建一个public static int n1 = 100;
* main中只写了 System.out.println(GirlFriend.n1);
* 这个时候gf对象已经创建好了
*/
class GirlFriend {
public static int n1 = 100;
private String name;
private static GirlFriend gf = new GirlFriend("小红红"); // 为了能够在静态方法中返回需要修饰为静态
// 即没有创建对象就可以使用这个方法
private GirlFriend(String name) {
this.name = name;
}
public static GirlFriend getInstance() {
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
package com.alice.day.day513;
public class SingletonDesignPattern03 {
public static void main(String[] args) {
System.out.println(Cat.n1); // 虽然加载了类,但是没有new
Cat cat = Cat.getInstance();
System.out.println(cat);
Cat cat1 = Cat.getInstance();
System.out.println(cat1);
System.out.println(cat == cat1); // 将上一次对象返回
}
}
/*
饿汉式式没有使用也给你创建实例
懒汉式是使用的时候才给你创建实例
所以饿汉式的对象一般都是重量级的对象,因为创建了不使用就会造成创建了对象但是没有时候的空间浪费
*/
class Cat {
// 我们希望在程序运行过程中只能创建一个Cat对象
// 也使用单例设计模式
private String name;
public static int n1 = 999;
// 仍然将构造器私有化防止new
private static Cat cat;
public Cat(String name) {
this.name = name;
}
public static Cat getInstance() {
if (cat == null) { //如果还没有创建cat对象
cat = new Cat("小可爱");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
/*
饿汉式和懒汉式的对比
1、二者最主要的区别式创建对象的时机式不同的:饿汉式式在类加载就创建了对象实例,而懒汉式是在使用的时候才创建
2、饿汉式不存在线程安全问题,懒汉式存在线程安全性问题。(后面学习线程之后,会完善)
public static Cat getInstance() {
if (cat == null) { //如果还没有创建cat对象
cat = new Cat("小可爱");
}
return cat;
}
比如三个线程进来执行,这个这个瞬间都判断没有创建对象,这样三个线程都会创建对象
只不过保留的是最后那个线程创建的对象而已
3、饿汉式存在浪费资源的可能,因为如果程序员没有使用对象实例,那么懒汉式创建的对象就浪费了,懒汉式是使用
才创建,就不存在这个问题。
4、在JavaSE标准类中,java.lang.Runtime就是经典的单例模式
小结:
1、单例模式的两种实现方式,第一种是饿汉式,第二种是懒汉式
2、饿汉式,创建了,式在类加载的时候就创建,可能存在资源浪费问题
3、懒汉式的问题:线程安全问题,后面学习了线程之后再进行完善。
4、要求可以独立写出单例模式。
*/
三、final
package com.alice.day.day513;
public class FinalCode04 {
public static void main(String[] args) {
// E e = new E();
// System.out.println(e.TAX_RATE);
// e.TAX_RATE = 10; // 不能被修改
}
}
/*
final中文意思是:最后的,最终的。
final可以修饰类、属性,方法和局部变量。
在某些情况下,程序员可能有以下需求,就会使用到final:
1、当不希望类被继承的时候可以使用final修饰
2、当不希望父类的某个方法被子类覆盖/重写Override的时候,可以使用final关键字
3、当不希望类的某个属性的值被修改,可以使用final修饰。
4、当不希望某个局部变量被修改,可以使用final修饰。
*/
///**
// * 不希望别的类继承,因为这个类很重要
// */
////final class A{}
////class B extends A{}如果是这样这里就会报错
//class C {
// // 如果我们要求hi方法不能被子类重写
// // 可以使用final修饰 hi方法
// public final void hi() {
// }
//}
//
//class D extends C {
//
// public void hi() {
// System.out.println("重写了C类的hi方法...");
// }
//}
///**
// * 不能被修改
// */
//class E {
// public final double TAX_RATE = 0.08;
//
// private void ee() {
// TAX_RATE = 10;
// }
//}
///**
// * 修饰局部不能修改
// */
//class F {
// public void ff(final int VALUE_F) {
// VALUE_F = 100;
// final double FF = 10.0;
// FF = 100;
// }
//}
package com.alice.day.day513;
public class FinalDetail05 {
public static void main(String[] args) {
C c = new C();
new EE().cal();
}
}
/*
final使用注意事项和细节讨论
FinalDetail
1、final修饰的属性又叫做常量,一般使用XX_XX_XX来命名
2、final修饰的属性在定义的时候,必须赋初值,并且以后不能再修改,赋值可以加在如下的位置之一,选择一个位置赋初值
即可
定义的时候,比如public final double TAX_RATE = 0.08;
在构造器中
在代码块中
3、如果final修饰的属性是静态的,则初始化的位置只能是
定义的时候,在静态代码块中不能在构造器中赋值
4、final类不能继承,但是可以实例化对象。
5、如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
*/
/**
* 1、final修饰的属性可以在定义的时候赋值
* 2、可以在代码块中赋值
* 3、可以在构造器中赋值
* final必须赋值
* 赋值后不能修改
*/
class AA{
public final double TAX_RATE = 0.08; // 在定义的时候赋值
public final double TAX_RATE2;
public final double TAX_RATE3;
public AA() { // 在构造器中赋值
TAX_RATE2 = 1.1;
}
{
// 在代码块中赋值
TAX_RATE3 = 8.8;
}
}
/**
*如果final修饰的属性是静态的,那么可以在以下三个地方进行赋值
* 1、定义的时候
* 2、静态代码块中
* 不能在构造器中赋值
* 为什么不能在构造器中赋值呢?因为构造器是创建对象的,而静态的属性是在类加载的时候
* 所以不行
*/
class BB {
public static final double TAX_RATE = 99.9; // 在定义的时候赋值
public static final double TAX_RATE2;
// public static final double TAX_RATE3;
public BB() {
// TAX_RATE3 = 8.8; // 不能在构造器中赋值
}
static {
TAX_RATE2 = 3.3; // 在静态代码块中赋值
}
}
// final类不能继承,但是可以实例化对象的
final class C{}
class DD {
public final void cal() {
System.out.println("cal()方法");
}
}
class EE extends DD{
// 可以继承下来使用,不能重写但是可以使用
}
package com.alice.day.day513;
public class FinalDetail06 {
public static void main(String[] args) {
System.out.println(BBB.num);
}
}
/*
final的细节第二部分,
6、一般来说,如果一个类已经是final类了,就没有必要再将方法修饰称final方法的必要。
比如写了一个类,这个类是一个final类,然后一个方法是final方法,这个类是不能被继承,所以方法不能够被继承
所以final修饰方法表示这个方法不能继承,这两个功能其实就是一个画蛇添足了。所以方法就不用添加final了.
final class AAA{
public final void cry() {}
},不过加上去还是可以.
7、final不能修饰构造方法
final 方法:防止子类重写。构造器:子类根本不能继承或重写父类构造器,所以 final 修饰构造器是多余的。
8、final和static往往搭配使用,效率更高,底层编译器做了优化处理。
比如现在类里面有一个静态的属性以及静态的代码块,如果我们调用这个类的这个属性的时候肯定会导致类的加载,
但是如果我们在这个静态属性加上一个final就不会导致类的加载,也就是说这个静态代码块不会被执行了.
9、包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
也就是说这些类都不能被继承.
*/
class BBB {
public final static int num = 10000;
static {
System.out.println("BBB 静态代码块被执行");
}
}
package com.alice.day.day513;
public class FinalExercise07 {
public static void main(String[] args) {
Circle1.s(33);
Circle2.s(33);
new Circle3().s(33);
}
}
/**
* 第一题,请编写一个程序,能够计算圆形的面积,要求圆周率为3.14,赋值的位置有三个
* 三个方式都写出来.
* 写一个类,类中有半径普通
* final属性pi
* 一个构造器,在构造器中赋值,或者是定义的时候赋值,还有一种是在代码块中赋值
*/
// 第一个位置
class Circle1 {
private static final double PI = 3.14;
public static void s(double r) {
System.out.println(PI * r * r);
}
}
// 第二个位置
class Circle2 {
private static final double PI;
static {
PI = 3.14;
}
public static void s(double r) {
System.out.println(PI * r * r);
}
}
// 第三个位置
class Circle3 {
private final double PI;
public Circle3 () {
PI = 3.14;
}
public void s(double r) {
System.out.println(PI * r * r);
}
}
// 第二道题,程序阅读题
class Something {
public int addOne(final int x) { // 可以的
// ++x; // 这里尝试对一个常量进行修饰,错误
return x + 1; // 这是是对返回结果进行处理没有关系
}
}
四、抽象类
package com.alice.day.day513;
public class Abstract08 {
public static void main(String[] args) {
}
}
/*
根据一个问题引出抽象类
有一个动物类,
属性name,age
构造器
吃东西行为,对于动物来说我们知道eat方法一般是子类重写,因为不同动物吃的什么不一样
即:父类方法的不确定性
当父类的某些方法,需要生命的时候,但是又不能确定如何实现的时候,可以将其声明为抽象方法,这个类就是抽象类了
*/
abstract class Animal {
private String name;
private int age;
public Animal(String name) {
this.name = name;
}
// public void eat() { // 这里实现了但是没有什么意义,这里就出现了父类方法不确定性问题
// // 考虑将该方法设计为抽象方法,abstract是抽象的意思,需要添加这个关键字,
// // 所谓抽象方法就是没有实现的方法,什么是没有实现呢?就是指的是没有方法体.
// System.out.println("这是一个动物类,但是不知道吃什么");
// }
public abstract void eat(); // 一旦一个类中存在了抽象方法之后,这个类就需要使用这个关键字声明为抽象类.
// 一般来说,抽象类会被继承,由该类的子类来实现抽象方法.
}
package com.alice.day.day513;
/*
抽象类的介绍
1\使用abstract关键字来修饰一个类的时候,这个类就叫做抽象类.
访问修饰符 abstract 类名 {}
2\使用abstract关键字来修饰一个方法的时候,这个方法就是抽象方法.
访问修饰符 abstract 返回类型 方法名(参数列表); // 没有方法体
3\抽象类的价值更多的是在于设计,是设计者设计好之后,让子类继承来实现.
4\抽象类是考官比较爱问的知识点,在框架和设计模式使用比较多.
抽象类细节比较多一共又八个点
抽象类使用的注意事项和细节说明
1\抽象类不能被实例化
2\抽象类不一定要包含abstract方法,也就是说,抽象类中可以没有abstract方法
3\一旦类中包含了abstract方法,则这个类必须声明为abstract
4\abstract只能修饰类和方法,不能修饰属性和其他的.
*/
//// 1\抽象类不能被实例化
//public class AbstractDetail09 {
// public static void main(String[] args) {
// new AAA(); // 抽象类不能被实例化
// }
//}
//abstract class AAA{}
//// 2\抽象类可以没有抽象方法
//abstract class AAA{
// public void hi(){
// System.out.println("你也好");
// }
//} // 抽象类可以没有抽象方法,有实现方法都可以
// 3\一旦类包含了abstract方法,则这个类也必须声明为抽象的.
//abstract class AAA{
// abstract public void hi();
//}
// abstract只能修饰类和方法,不能修饰属性和其他的.
package com.alice.day.day513;
public class AbstractDetail10 {
public static void main(String[] args) {
}
}
/*
抽象类可以又任意的成员,因为抽象类还是类,比如:非抽象方法,构造器,静态属性等等,
抽象方法不能有主体,即不能实现
如果一个类继承了抽象类,则它必须实现抽象类中的所有抽象方法,除非它自己也声明为abstract类.
抽象方法不能使用private,final和static来修饰,因为这些关键字都是和重写相违背的.
*/
// 1\
abstract class D{
public int n1 = 10;
public static String name = "你好呀";
public void h1() {
System.out.println("h1");
}
public abstract void hello();
public static void ok() {
System.out.println("ok");
}
}
// 3
abstract class E{
public abstract void hi();
}
abstract class F extends E {
// 除非自己也是一个抽象类
}
class G extends E {
// 或者自己实现
@Override
public void hi() {
// 实现就是要有方法体,具体怎么实现不关心.
}
}
package com.alice.day.day513.test20;
//public class AbstractExercise01 {
// public static void main(String[] args) {
//// A a = new B();
//// a.test2(); // 我们知道动态绑定调用的应该是运行时类型的
////
//// a.test3(); // 这里动态绑定机制,调用的是B类重写的方法
////
//// a.test4(); // 通过动态绑定机制调用了子类的方法
// }
//}
/*
课堂练习
AbstractExercise01
第一题:
思考 abstract final class A{}能编译通过吗?为什么?
首先final修饰的类不能被继承,而abstract修饰的类需要被继承实现,所以不能通过
第二题:
思考 abstract public static void test2(); 能编译通过吗?为什么?
abstract没有具体的实现,需要子类继承之后重写实现
通过动态绑定机制调用子类的方法,体现了多态。
static是和类绑定的,不和实例绑定,可以通过类名直接调用
不能被重写,子类可以声明同名的静态方法,这里隐藏了而非重写,不能体现多态。
即出现了矛盾,一个可以重写,一个不能重写,一个体现了多态,一个3不体现多态,所以不行。
即abstract动态绑定机制,一个static是编译绑定
第三题:
思考 abstract private void test3(); 能通过编译吗?为什么,
这里因为abstract定义的是一个抽象方法,需要被子类继承重写实现,而这里却是
一个private的访问权限,说明子类不能访问这个方法,所以这里产生了语法错误不能通过编译。
*/
// 第一题
//abstract final class A{} // 产生语法错误不能编译通过
//// 第二题
//abstract class A {
// public static void test2(){
// System.out.println("你好,我是A");
// }
//
// public void test3() {
// System.out.println("你好,我是A中的普通成员方法,我可以重写");
// }
//
// abstract public void test4();
//}
//class B extends A {
// public static void test2(){ // 这样看起来是重写了,但是其实没有实现重写
// System.out.println("你好,我是B");
// }
//
// @Override
// public void test3() {
// System.out.println("你好,我是B类中的普通成员方法,我重写了A的普通成员方法");
// }
//
// @Override
// public void test4() {
// System.out.println("这里是B,重写了A的test4");
// }
//}
public class AbstractExercise01 {
public static void main(String[] args) {
Manager jack = new Manager("jack", "999", 50000, 8000);
jack.work();
CommonEmployee tom = new CommonEmployee("tom", "888", 20000);
tom.work();
}
}
/*
第四题:
编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。
提供必要的构造器和抽象方法:work()。对于Manager类来说,它既是员工,还是
具有奖金的bonus属性,请使用继承的思想,设计CommonEmployee类和Manager类
,要求类中提供必要的方法进行属性访问,实现work(),提示
“经理/普通员工 名字 工作中。。。”
*/
abstract class Employee {
private String name;
private String id;
private double salary;
public Employee(String name, String id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
abstract public void work();
}
class Manager extends Employee{
private double bonus;
public Manager(String name, String id, double salary, double bonus) {
super(name, id, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("经理" + getName() + "工作中"); // 经理/普通员工 名字 工作中。。。
}
}
class CommonEmployee extends Employee {
public CommonEmployee(String name, String id, double salary) {
super(name, id, salary);
}
@Override
public void work() {
System.out.println("普通员工" + getName() + "工作中");
}
}
package com.alice.day.day513.test20;
/*
抽象类在什么地方会使用到呢?
在模板设计模式这里会使用到,这是一个非常经典的应用场景
也就是设计模式里面的模板模式
现在给出一个需求:
1、有多个类,需要完成不同的任务job
2、要求能够得到各自完成任务的时间
3、请编程实现 TestTemplate.java
*/
public class AbstractBestPractices02 {
public static void main(String[] args) {
A a = new A();
a.calculateTime();
B b = new B();
b.calculateTime();
}
}
class A extends Template{
public void job() {
long num = 0;
for (int i = 1; i < 8000000; i++) { // 可以通过10000.for回车自动生成
num += i;
}
}
} // 在A中写一个job方法计算1加到8000000的总和
class B extends Template{
public void job() {
long num = 0;
for (int i = 1; i < 800000; i++) { // 可以通过10000.for回车自动生成
num *= i;
}
}
} // 我们需要统计一个*的方式800000
//AA执行时间是:5
//BB执行时间是:2
/*
发现我们的类中有了重复的代码,也就是我们计算时间的代码
如果现在还有一个另外的工作,统计1+10等的工作
这样代码复用性非常差,我们需要将公用的代码提取出来
将统计开始时间,结束时间,以及打印总执行时间的部分,拿出来
而中间执行的部分我们直接调用即可。
public void calculateTime() {
// 得到开始的时间
long start = System.currentTimeMillis();
job();
// 得到的结束时间
long end = System.currentTimeMillis();
System.out.println("AA执行时间是:" + (end-start));
}
public void job() {
long num = 0;
for (int i = 1; i < 8000000; i++) { // 可以通过10000.for回车自动生成
num += i;
}
}
同理B中我们也可以这样做
这个时候我们A B中都有这样统计时间的一个方法
这个地方就方便了,将共有的部分提取出来,产生了新的方法
这个抽取出来的方法,这两个是一样的,以后我们都这样做吗
这个时候就需要提出观点,抽象一个父类,父类中这个地方就是一个模板类
写成一个抽象的方法,将这些计算时间的方法抽象到父类的方法中
形成一个模板设计模式。
*/
abstract class Template { // 抽象类,模板设计模式
public abstract void job(); // 抽象方法
public void calculateTime() { // 实现了的方法,调用job方法即可
// 得到开始的时间
long start = System.currentTimeMillis();
job();
// 得到的结束时间
long end = System.currentTimeMillis();
System.out.println("执行时间是:" + (end-start));
}
}
五、接口
package com.alice.day.day513.test20.interface03_;
public class Interface03 {
}
/*
U盘,摄像机,手机,我们日常生活中可能经常插入到电脑上面使用
这里的USB就是现实生活中的一个接口
USB,插槽就是现实生活中的接口
你可以将手机,相机,U盘都插在USB插槽上,不用担心那个插槽是专门插哪个的,原因是做usb插槽的场景和做各种设备的厂家
都遵循了统一的规定包括尺寸,排线等等。
我们使用编程来模拟这样的一个情况
*/
package com.alice.day.day513.test20.interface03_;
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);
}
}
package com.alice.day.day513.test20.interface03_;
public class Phone implements UsbInterface{
// Phone类实现UsbInterface
// Phone需要实现UsbInterface的接口
@Override
public void start() {
System.out.println("手机开始工作...");
}
@Override
public void stop() {
System.out.println("手机停止工作...");
}
}
package com.alice.day.day513.test20.interface03_;
public class Camera implements UsbInterface{ // 机器实现接口
@Override
public void start() {
System.out.println("相机开始工作...");
}
@Override
public void stop() {
System.out.println("相机停止工作...");
}
}
package com.alice.day.day513.test20.interface03_;
public class Computer {
// 编写一个方法,计算机工作
public void work(UsbInterface usbInterface) {
usbInterface.start();
usbInterface.stop();
}
}
package com.alice.day.day513.test20.interface03_;
public interface UsbInterface { // 接口,比较像现实生活中的规范
// 规定接口的相关方法
public void start();
public void stop();
}
package com.alice.day.day513.test20.interface04_;
public class interface04 {
public static void main(String[] args) {
}
}
/*
接口的基本介绍:
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况将这些方法写出来。
语法:
interface 接口名 {
// 属性
// 方法
}
class 类名 implements 接口 {
自己属性;
自己方法;
必须实现的接口的抽象方法
}
在接口中抽象方法可以省略abstract关键字
小结:
在JDK7.0之前,接口里面的所有方法都没有方法体,
JDK8.0之后接口类可以有静态方法,默认方法,也就是说接口中可以又方法的具体实现
*/
interface AInterface {
// 写属性
public int n1 = 10;
// 写方法
// 在接口中,抽象方法,可以省略abstract关键字
public void hi();
// j8后可以又默认实现方法,但是需要使用default关键字修饰
// 另外还可以又静态方法
default public void ok() {
System.out.println("ok......");
}
public static void cry() {
System.out.println("cry ........");
}
}
/**
* 1、如果是一个类 implements实现接口
* 2、需要将该接口的所有抽象方法实现
*/
class A implements AInterface {
@Override
public void hi() {
System.out.println("hi()......");
}
}
package com.alice.day.day513.test20.interface05_;
public class Interface05 {
public static void main(String[] args) {
MysqlDB mysqlDB = new MysqlDB();
t(mysqlDB);
OracleDB oracleDB = new OracleDB();
t(oracleDB);
}
public static void t(DBInterface db) {
db.connect();
db.close();
}
}
/*
接口对于初学者来说,理解接口的概念不算太难,难的是不知道什么时候使用接口,下面我们通过几个例子来举例说明
1、现在要制造战斗机,武装直升机,专家只需要将飞机所需要的功能/规格定下来即可,然后让别人具体实现就好。
2、现在有一个项目经理,管理三个程序员,功能开发一个软件,为了控制和管理软件,项目经理可以定义一些接口
然后由程序员具体实现
直接写类不就行了,为什么还需要写接口呢?
但是谁来规范这些写出来的类呢?没有规范控制软件的质量和规范,不能控制功能,接口还涉及到一个统一调用的问题。
比如现在我们有一个实际的要求,要求三个程序员编写三个类分别完成对mysql,oracle,DB2数据库的链接。
这三个程序员分别写的方法名称比如:
A程序员链接MySQL
f1() f2()
B程序员链接Oracle
con()
close()
C程序员链接DB2
connect()
shutdown()
每个人编写的名字都不同,如果这个时候使用接口来规范这些名字就会非常方便。
*/
interface DBInterface { // 项目经理
public void connect(); // 链接方法
public void close(); // 关闭链接
}
class MysqlDB implements DBInterface {
// A程序员实现接口
@Override
public void connect() {
System.out.println("链接mysql");
}
@Override
public void close() {
System.out.println("关闭mysql");
}
}
// B程序员链接Oracle
class OracleDB implements DBInterface {
@Override
public void connect() {
System.out.println("连接oracle");
}
@Override
public void close() {
System.out.println("关闭oracle");
}
}
package com.alice.day.day513.test20.interface06_;
//public class Interface06 {
// public static void main(String[] args) {
// new IA(); // 1、接口不能被实例化
// }
//}
//interface IA {}
/*
关于接口的注意事项和细节
1、接口不能被实例化
2、接口中的所有方法是public方法,接口中的抽象方法,可以不用abstract修饰
3、一个普通类实现接口,就必须将该接口的所有方法都实现。
4、抽象类实现接口,可以不用实现接口的方法。
*/
// 2、接口中所有的方法都是public方法,接口中抽象方法,可以不使用abstract修饰
// 设置成其他的访问修饰符不可以
//interface IA {
// void say();
// // 这里默认都是public的,默认也是abstract
// // public abstract void say();
//}
//// 3、一个普通类实现接口,就必须将该接口的所有方法都实现
//interface IA {
// void say();
// void hi();
//}
//class Cat implements IA { // 出现红色波浪线的时候可以直接Alt + Enter快速生成重写代码
//
// @Override
// public void say() {
//
// }
//
// @Override
// public void hi() {
//
// }
//}
// 4、抽象类可以不用实现
interface IA {
void say();
void hi();
}
abstract class Tiger implements IA {}
package com.alice.day.day513.test20.interface07_;
/*
接口的细节
5、一个类同时可以实现多个接口
6、接口中的属性,只能是final的,而且是public static final 修饰符,比如:
int a = 1;实际上是public static final int a = 1;(必须初始化)
7、接口中属性的访问形式:接口名.属性名
8、一个接口不能继承其他的类,但是可以继承多个别的接口
9、接口的修饰符只能是public和默认,这一点和类的修饰符是一样的。
*/
//// 1、一个类可以同时实现多个接口
//interface IB {
// void hi();
//}
//interface IC {
// void say();
//}
//class Pig implements IB,IC {
//
// @Override
// public void hi() {
//
// }
//
// @Override
// public void say() {
//
// }
//}
//// 2、int n1 = 10等价于 public static final int n1 = 10;
//public class Interface07 {
// public static void main(String[] args) {
// System.out.println(IB.n1); // 这里证明是static的
// // IB.n1 = 30; // 这里证明是final的
// // 而要判断是public的不是默认的只需要通过javap反编译或者是在另外一个包中访问即可,如果能够访问就是public
// }
//}
//interface IB {
// // 接口中的属性,只能是final的,并且是public static final 修饰符
// int n1 = 10;
//}
//// 3、接口不能继承其他的类,但是可以继承多个别的接口
//interface IB {}
//interface IC {}
//interface ID extends IB, IC {}
package com.alice.day.day513.test20.interface08_;
public class InterfaceExercise01 {
public static void main(String[] args) {
B b = new B();
System.out.println(b.a); // 通过实例对象调用继承过来的属性是可以的
System.out.println(A.a); // 通过A或者B这种“类名”调用也是可以的。
System.out.println(B.a);
}
}
interface A { // 接口也可以理解为一个类
int a = 23;
// 首先这里等价于public static final int a = 23;
}
class B implements A{ // 使用类去实现接口,这个是可以的。实现也可以粗略的理解为继承
// 语法是否正确,如果正确,输出什么?
// 这里因为接口没有抽象方法,所以这里不需要实现。
}
package com.alice.day.day513.test20.interface09_;
/*
接口和继承类的比较
实现接口和继承类又什么区别呢?
就像你继承你爹的特性,你爹的遗产
你如果想像小鸟一样飞翔就需要制作飞行器来实现
如果想要像小鱼一样游泳,就需要制作潜水艇来实现
理解:实现机制是对于单继承机制的补充
*/
public class Interface09 {
public static void main(String[] args) {
LittleMonkey wuKong = new LittleMonkey("悟空");
wuKong.climbing();
wuKong.swimming();
wuKong.flying();
}
}
// 创建一个小猴子类
class LittleMonkey extends Monkey implements Fishable,Birdable{
public LittleMonkey(String name) {
super(name);
}
@Override
public void swimming() {
System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳。");
}
@Override
public void flying() {
System.out.println(getName() + " 通过学习,可以像小鸟一样飞翔。");
}
}
// 创建一个老猴子类
class Monkey {
private String name;
public Monkey(String name) {
this.name = name;
}
public void climbing() {
System.out.println(name + " 会爬树...");
}
public String getName() {
return name;
}
}
// 创建一个鱼的接口(功能)
interface Fishable {
void swimming();
}
interface Birdable {
void flying(); // 还可以像小鸟一样飞翔
}
// 小结:
// 当子类继承了父类,就自动拥有了父类的功能
// 如果子类需要扩展功能,可以通过实现接口的方式来扩展,
// 实现接口是对Java单继承机制的一种补充。
/*
总结:
实现接口和继承类
接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值在于:设计,设计好各种的规范(方法),让其他类去实现这些方法。
接口比继承更加灵活,接口比继承更加灵活,继承是满足is - a的关系,而接口只需满足lik-a的关系。
接口在一定程度上实现代码的解耦(接口的规范性+动态绑定)
*/
package com.alice.day.day513.test20.interface10_;
//public class interface10 {
// // 接口的多态体现
// // 接口类的变量if01可以指向实现了IF接口的对象实例
// public static void main(String[] args) {
// IF if01 = new Monster();
// if01 = new Car();
// }
//}
//interface IF {}
//class Monster implements IF {}
//class Car implements IF {}
/*
接口
接口的多态特性
1、多态参数
在前面的时候使用Usb接口案例,Usb usb,既可以接收手机对象,又可以接收相机对象,这就体现了接口多态
(接口引用可以指向实现了接口的类的对象)
2、多态数组
演示一个案例:给Usb数组中,存放Person和相机对象,Phone类中还有一个特定的方法call
,请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法之外,还需要调用Phone特有的方法call。
3、接口存在多态传递现象。InterfacePolyPass.java
*/
//public class interface10 {
//
// public static void main(String[] args) {
// 继承体现的多态
// 父类类型的变量a可以指向继承AAA的子类的对象实例
// AAA a = new BBB();
// a = new CCC();
// }
//}
//class AAA {}
//class BBB extends AAA {}
//class CCC extends AAA {}
public class interface10 {
public static void main(String[] args) {
// 多态数组->接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone_();
usbs[1] = new Camera_();
for (int i = 0; i < usbs.length; i++) {
usbs[i].work(); // 动态绑定机制
// 如果需要一个执行特定的方法需要进行一个类型的判断
if (usbs[i] instanceof Phone_) {
// 判断运行类型是否为Phone_
((Phone_) usbs[i]).call(); // 将编译类也变成这个就行了
}
}
}
}
interface Usb{
void work();
}
class Phone_ implements Usb {
public void call() {
System.out.println("手机可以打电话...");
}
@Override
public void work() {
System.out.println("手机工作中...");
}
}
class Camera_ implements Usb {
@Override
public void work() {
System.out.println("相机工作中...");
}
}
package com.alice.day.day513.test20.interface11_;
public class Interface11 {
public static void main(String[] args) {
// 接口类型的变量可以指向,实现了该接口的类的对象实例
IG ig = new Teacher();
IH ih = new Teacher(); // 所以IH也能够指向,因为接口IG继承了IH
}
}
interface IH {
void hi();
}
interface IG extends IH {} // 又因为IG继承了IH,所以也就相当于Teacher实现了IH接口
class Teacher implements IG { // 也要实现IH,这就是多态传递现象
@Override
public void hi() {
System.out.println("实现IH的hi方法");
}
}
/*
接口的多态特性
第三个:接口存在多态传递现象,
*/
package com.alice.day.day513.test20.interface12_;
public class Interface12 {
public static void main(String[] args) {
new C().pX();
}
}
interface A{
int x = 0; // 这里相当于是public static final int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
// System.out.println(x); // 这里继承的B类中又一个x,要实现的接口A中也有一个x,不知道是哪一个
// 所以就需要指明使用哪一个的x
// 如果是访问接口的x,就使用A.x
// 如果是访问父类的x,就是用super.x
System.out.println(A.x + " " + super.x);
}
}
// 看看有没有错误,有错误就修改,改好之后看看输出。
/*
回顾:
成员变量,成员方法,构造方法,包,继承,接口,代码块等等
下一个阶段开始学习内部类,内部类是我们程序员的一个分水岭
内部类学好,对于面向对象的认识将再上一个台阶。
*/