哈喽!大家好,我是小简。今天开始学习《Java-面向对象》,此系列是我做的一个 “Java 从 0 到 1 ” 实验,给自己一年左右时间,按照我自己总结的 Java-学习路线,从 0 开始学 Java 知识,并不定期更新所学笔记,期待一年后的蜕变吧!<有同样想法的小伙伴,可以联系我一起交流学习哦!>
类与对象
成员方法
方法递归
方法重载
可变参数
作用域
构造方法
对象创建
this关键字
包
访问修饰符
封装
继承
super关键字
重写&重载
请编写一个程序,Master 类中有一个 feed 方法, 可以完成主人给动物喂食物的信息。
使用传统的方法来解决带来的问题是什么? 如何解决?
public class Poly_ {
public static void main(String[] args) {
Master tom = new Master("Tom");
Dog jack = new Dog("jack");
Cat jerry = new Cat("jerry");
Bone bone = new Bone("大骨头");
Fish fish = new Fish("小鱼");
tom.feed(jack,bone);
tom.feed(jerry,fish);
}
}
class Master {
private String name;
public Master(String name) {
this.name = name;
}
//主人给小狗喂食骨头
public void feed(Dog dog, Bone bone) {
System.out.println("主人" + name + " 给" + dog.getName() + " 吃" + bone.getName());
}
//主人给小猫喂黄花鱼
public void feed(Cat cat, Fish fish) {
System.out.println("主人" + name + " 给" + cat.getName() + " 吃" + fish.getName());
}
}
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
class Food {
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Bone extends Food {
public Bone(String name) {
super(name);
}
}
class Fish extends Food {
public Fish(String name) {
super(name);
}
}
带来的问题:
解决方案: 采用多态解决
public class Poly_ {
public static void main(String[] args) {
Master tom = new Master("Tom");
Dog jack = new Dog("jack");
Cat jerry = new Cat("jerry");
Bone bone = new Bone("大骨头");
Fish fish = new Fish("小鱼");
tom.feed(jack,bone);
tom.feed(jerry,fish);
}
}
class Master {
private String name;
public Master(String name) {
this.name = name;
}
//使用多态机制,可以统一的管理主人喂食的问题
//animal 编译类型是Animal,可以指向(接收) Animal 子类的对象
//food 编译类型是Food ,可以指向(接收) Food 子类的对象
public void feed(Animal animal, Food food) {
System.out.println("主人" + name + " 给" + animal.getName() + " 吃" + food.getName());
}
}
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
class Food {
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Bone extends Food {
public Bone(String name) {
super(name);
}
}
class Fish extends Food {
public Fish(String name) {
super(name);
}
}
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
重载和重写都体现了多态
一个对象的编译类型和运行类型可以不一致
编译类型在定义对象时,就确定了,不能改变
运行类型是可以变化的
编译类型看定义时=号的左边,运行类型看=号的右边
Animal animal = new Dog();//animal编译类型是Animal,运行类型Dog
animal = new Cat(); //animal的运行类型变成了Cat, 编译类型仍然是Animal
多态的前提是:两个对象(类)存在继承关系
1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名 = new 子类类型();
Animal animal = new Dog();
3)特点:编译类型看左边,运行类型看右边
public class PolyDetail {
public static void main(String[] args) {
//向上转型: 父类的引用指向了子类的对象
Animal_ animal = new Cat_();
//可以调用父类中的所有成员(需遵守访问权限)
animal.eat();//吃
animal.sleep();//睡
//不能调用子类中特有成员(属性/方法)
animal.catchMouse();//错误写法
}
}
class Animal_ {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void eat(){
System.out.println("吃");
}
}
class Cat_ extends Animal_ {
@Override
public void eat(){//方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){//Cat 特有方法
System.out.println("猫抓老鼠");
}
}
1)语法:子类类型引用名 = (子类类型) 父类引用;
Dog dog = (Dog) animal;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员
public class PolyDetail {
public static void main(String[] args) {
Animal_ animal = new Cat_();
//向下转型:希望可以调用Cat的catchMouse 方法
Cat_ cat = (Cat_) anima;
cat.eat();//吃
cat.sleep();//睡
cat.catchMouse();//特有方法也可调用
//要求父类的引用必须指向的是当前目标类型的对象
Dog dog = (Dog) animal; //错误写法
}
}
class Animal_ {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void eat(){
System.out.println("吃");
}
}
class Cat_ extends Animal_ {
@Override
public void eat(){//方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){//Cat 特有方法
System.out.println("猫抓老鼠");
}
}
class Dog_ extends Animal_ {//Dog 是Animal 的子类
}
属性没有重写之说!属性的值看编译类型
public class PolyDetail02 {
public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
Base base = new Sub();//向上转型
System.out.println(base.count);//看编译类型10
Sub sub = new Sub();
System.out.println(sub.count);//20
}
}
class Base { //父类
int count = 10;//属性
}
class Sub extends Base {//子类
int count = 20;//属性
}
用于判断对象的运行类型是否为XX 类型或XX 类型的子类型
public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);//true
System.out.println(bb instanceof AA);//true
//BB 是AA 子类
AA aa = new BB(); //aa 编译类型AA, 运行类型是BB
System.out.println(aa instanceof AA);//true
System.out.println(aa instanceof BB);//true
Object obj = new Object();
System.out.println(obj instanceof AA);//false
String str = "hello";
System.out.println(str instanceof Object);//true
}
}
class AA {} //父类
class BB extends AA {}//子类
public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型A, 运行类型B
A a = new B();//向上转型
//运行类型是B,先找B类里是否有sum,有就直接运行
System.out.println(a.sum());//40 调用子类sum
System.out.println(a.sum1());//30 调用子类sum1
}
}
class A {//父类
public int i = 10;
public int sum() {//父类sum()
return getI() + 10;
}
public int sum1() {//父类sum1()
return i + 10;
}
public int getI() {//父类getI
return i;
}
}
class B extends A {//子类
public int i = 20;
@Override
public int sum() {
return i + 20;
}
@Override
public int getI() {//子类getI()
return i;
}
@Override
public int sum1() {
return i + 10;
}
}
public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型A, 运行类型B
A a = new B();//向上转型
//运行类型是B,先找B类里是否有sum,没有就去父类找
System.out.println(a.sum());//30 调用父类sum 和子类getI
System.out.println(a.sum1());//20 调用父类sum 和父类i
}
}
class A {//父类
public int i = 10;
//动态绑定机制
public int sum() {//父类sum()
return getI() + 10;//20 + 10
}
public int sum1() {//父类sum1()
return i + 10;//10 + 10
}
public int getI() {//父类getI
return i;
}
}
class B extends A {//子类
public int i = 20;
@Override
public int getI() {//子类getI()
return i;
}
}
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
应用实例:现有一个继承结构如下:要求创建1 个Person 对象、2 个Student 对象和2 个Teacher 对象, 统一放在数组中,并调用每个对象 say 方法,如何调用子类特有的方法,比如 Teacher 有一个 teach , Student 有一个 study
怎么调用?
public class PolyArray {
public static void main(String[] args) {
//应用实例:现有一个继承结构如下:要求创建1 个Person 对象、
// 2 个Student 对象和2 个Teacher 对象, 统一放在数组中,并调用每个对象say 方法
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("mary", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
//循环遍历多态数组,调用say
for (int i = 0; i < persons.length; i++) {
//person[i] 编译类型是Person ,运行类型是是根据实际情况有JVM 来判断
System.out.println(persons[i].say());//动态绑定机制
// 使用类型判断+ 向下转型
if(persons[i] instanceof Student) {//判断person[i] 的运行类型是不是Student
Student student = (Student)persons[i];//向下转型
student.study();
//也可以使用一条语句((Student)persons[i]).study();
} else if(persons[i] instanceof Teacher) {
Teacher teacher = (Teacher)persons[i];
teacher.teach();
} else if(persons[i] instanceof Person){
System.out.println("不做处理...");
} else {
System.out.println("你的类型有误, 请自己检查...");
}
}
}
}
class Person {//父类
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public String say() {//返回名字和年龄
return name + "\t" + age;
}
}
class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
//重写父类say
@Override
public String say() {
return "学生" + super.say() + " score=" + score;
}
//特有的方法
public void study() {
System.out.println("学生" + getName() + " 正在学java...");
}
}
class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
//写重写父类的say 方法
@Override
public String say() {
return "老师" + super.say() + " salary=" + salary;
}
//特有方法
public void teach() {
System.out.println("老师" + getName() + " 正在讲java 课程...");
}
}
方法定义的形参类型为父类类型,实参类型允许为子类类型
应用实例: 定义员工类 Employee,包含姓名和月工资[private],以及计算年工资 getAnnual 的方法。普通员工和经理继承了员工,经理类多了奖金 bonus 属性和管理 manage 方法,普通员工类多了 work 方法,普通员工和经理类要求分别重写 getAnnual 方法,测试类中添加一个方法 showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在 main 方法中调用该方法[e.getAnnual()] 测试类中添加一个方法,testWork,如果是普通员工, 则调用 work 方法,如果是经 理,则调用 manage方法
public class PolyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 2500);
Manager milan = new Manager("milan", 5000, 200000);
PolyParameter.showEmpAnnual(tom);
PolyParameter.showEmpAnnual(milan);
PolyParameter.testWork(tom);
PolyParameter.testWork(milan);
}
public static void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
public static void testWork(Employee e) {
if (e instanceof Worker) {
((Worker) e).work();//有向下转型操作
} else if (e instanceof Manager) {
((Manager) e).manage();//有向下转型操作
} else {
System.out.println("不做处理...");
}
}
}
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public double getAnnual() {
return 12 * salary;
}
}
class Worker extends Employee {
public Worker(String name, double salary) {
super(name, salary);
}
public void work() {
System.out.println("普通员工" + getName() + " is working");
}
@Override
public double getAnnual() {
return super.getAnnual();
}
}
class Manager extends Employee {
private double bonus;
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public void manage() {
System.out.println("经理" + getName() + " is managing");
}
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
equals 是 Object 类中的方法,只能判断引用类型,默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如 Integer,String
== 和 equals 的对比
如何重写 equals 方法
🌰:判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回true,反之false
public class EqualsExercise01 {
public static void main(String[] args) {
Person person1 = new Person("jack", 10, '男');
Person person2 = new Person("jack", 20, '男');
System.out.println(person1.equals(person2));//假
}
}
//判断两个Person 对象的内容是否相等,
//如果两个Person 对象的各个属性值都一样,则返回true,反之false
class Person{ //extends Object
private String name;
private int age;
private char gender;
//重写Object 的equals 方法
public boolean equals(Object obj) {
//判断如果比较的两个对象是同一个对象,则直接返回true
if(this == obj) {
return true;
}
//类型判断
if(obj instanceof Person) {//是Person,我们才比较
//进行向下转型, 因为我需要得到obj 的各个属性
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
//如果不是Person ,则直接返回false
return false;
}
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
}
返回该对象的哈希码值。此方法是为了提高哈希表的性能。
当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作
什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize 方法。
垃圾回收机制的调用,是由系统来决定(即有自己的GC 算法), 也可以通过System.gc() 主动触发垃圾回收机制
public class Finalize_ {
//如果程序员不重写finalize,那么就会调用Object类的finalize, 即默认处理
//如果程序员重写了finalize, 就可以实现自己的逻辑
public static void main(String[] args) {
Car bmw = new Car("宝马");
//这时car对象就是一个垃圾
//垃圾回收器就会回收(销毁)对象,
//在销毁对象前,会调用该对象的finalize 方法
bmw = null;
System.gc();//主动调用垃圾回收器
System.out.println("程序退出了....");
}
}
class Car {
private String name;
public Car(String name) {
this.name = name;
}
//重写finalize
@Override
protected void finalize() throws Throwable {
System.out.println("我们销毁汽车" + name );
System.out.println("释放了某些资源...");
}
}
类变量(又叫静态变量)是该类的所有对象共享的变量
,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)。比如:定义学生类,统计所有学生共交多少钱。Student (name, static fee)
public class ChildGame {
public static void main(String[] args) {
//普通方法需定义一个变量 count, 统计有多少小孩加入了游戏
//int count = 0;
Child child1 = new Child("白骨精");
child1.join();
//count++;
child1.count++;
Child child2 = new Child("狐狸精");
child2.join();
//count++;
child2.count++;
Child child3 = new Child("老鼠精");
child3.join();
//count++;
child3.count++;
//类变量,可以通过类名来访问,也可通过对象名访问
System.out.println("共有" + Child.count + " 小孩加入了游戏...");
System.out.println("child1.count=" + child1.count);//3
System.out.println("child2.count=" + child2.count);//3
System.out.println("child3.count=" + child3.count);//3
}
}
class Child {
private String name;
public static int count = 0; //该变量最大的特点就是会被 Child 类的所有的对象实例共享
public Child(String name) {
this.name = name;
}
public void join() {
System.out.println(name + " 加入了游戏..");
}
}
类方法,无需创建类的对象即可对其进行访问
this
、super
;普通方法可以。(因为当用类名.类方法名
调用的时候,this 和 super 指示不明)package com.jwt.static_;
public class StaticMethod {
public static void main(String[] args) {
//创建两个学生交学费
Stu tom = new Stu("Tom");
tom.payFee(100);
Stu mary = new Stu("mary");
mary.payFee(200);
Stu.showFee();
}
}
class Stu{
private String name;
private static double fee = 0;//静态变量累积学费
public Stu(String name) {
this.name = name;
}
public static void payFee(double fee){//静态方法可访问静态变量
Stu.fee += fee;
}
public static void showFee(){
System.out.println("总学费:" + Stu.fee);
}
}
main 方法的形式: public static void main(String[] args){}
第一步 | 第二步 |
---|---|
代码块又称为 初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过 { } 包围起来,但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时或创建对象时隐式调用。
[修饰符]{
这里是代码;
};
说明:
首先看下面代码,三个构造器都有相同的语句,这样代码看起来比较冗余,如何简化代码呢?
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("你好,李焕英");
System.out.println("===============");
Movie movie2 = new Movie("唐探3", 100, "陈思诚");
}
}
class Movie {
private String name;
private double price;
private String director;
//3个构造器-》重载
public Movie(String name) {
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
System.out.println("Movie(String name) 被调用...");
this.name = name;
}
public Movie(String name, double price) {
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
System.out.println("Movie(String name, double price, String director) 被调用...");
this.name = name;
this.price = price;
this.director = director;
}
}
这时我们可以把相同的语句,放入到一个代码块中,这样当我们不管调用哪个构造器创建对象,都会先调用代码块的内容,代码块调用的顺序优先于构造器
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
};
改进后代码:
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("你好,李焕英");
System.out.println("===============");
Movie movie2 = new Movie("唐探3", 100, "陈思诚");
}
}
class Movie {
private String name;
private double price;
private String director;
//代码块
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
};
//3个构造器-》重载
public Movie(String name) {
System.out.println("Movie(String name) 被调用...");
this.name = name;
}
public Movie(String name, double price) {
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
System.out.println("Movie(String name, double price, String director) 被调用...");
this.name = name;
this.price = price;
this.director = director;
}
}
总结
总结: 1——根据static代码块是在类加载时被调用,当要创建子类这个对象时,发现这个类需要一个父类,所以把父类的 .class 加载进来,故先是父类的静态代码块或静态属性初始化; 2——然后是加载子类的类信息,故是子类的静态代码块或静态属性初始化; 3——然后走进子类构造器的super语句,进入父类的构造器,先super(),然后调用父类普通代码块,故是父类的普通代码块或普通属性初始化; 4——然后父类构造器调用完毕,即是父类的构造方法; 5——然后回到子类的构造方法中隐含的调用普通代码块或普通属性的初始化; 6——然后完成子类的构造器,故最后是子类的构造方法。
final 在 Java 中的意思是最终,也可以称为完结器,表示对象是最终形态的,不可改变的意思。final 可以修饰类、 属性、方法和局部变量,在某些情况下,就会使用到 final:
//1.如果我们要求A 类不能被其他类继承
//可以使用final 修饰A 类
final class A {
}
//class B extends A {} //会报错
class C {
//2.如果我们要求hi 不能被子类重写
//可以使用final 修饰hi 方法
public final void hi() {
}
}
class D extends C {
// @Override //会报错
// public void hi() {
// System.out.println("重写了C 类的hi 方法..");
// }
}
//3.当不希望类的的某个属性的值被修改,可以用final 修饰
class E {
public final double TAX_RATE = 0.08;//常量
}
//4.当不希望某个局部变量被修改,可以使用final 修饰
class F {
public void cry() {
//这时,NUM 也称为局部常量
final double NUM = 0.01;
//NUM = 0.9; //会报错
System.out.println("NUM=" + NUM);
}
}
注意事项
①定义时
public final double TAX RATE = 0.08;//定义时赋值
②在构造器中
public AA() {//构造器中赋值
TAX_RATE2 = 1.1;
}
③在代码块中
{//在代码块赋值
TAX_RATE3 = 8.8;
}
final 修饰的属性是静态的,则初始化的位置只能是
①定义时、在静态代码块
public static final double TAX_RATE = 99.9;
static {
TAX_RATE2 = 3.3;
}
②不能在构造器中赋值
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为 抽象方法, 那么这个类就是抽象类
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
//eat这里实现了,其实没有什么意义
public void eat() {
System.out.println("这是一个动物,但是不知道吃什么..");
}
}
考虑将该方法设计为抽象(abstract)方法,所谓抽象方法就是没有实现的方法,所谓没有实现就是指,没有方法体。当一个类中存在抽象方法时,需要将该类声明为 abstract 类, 一般来说,抽象类会被继承,由其子类来实现抽象方法。
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
//只声明不实现
public abstract void eat() ;
}
当父类的一些方法不能确定时,可以用 abstract 关键字来修饰该方法,这个方法 就是抽象方法,并且需要用 abstract 来修饰该类,这个类就叫抽象类。
1、用 abstract 关键字来修饰一个类时,这个类就叫抽象类
访问修饰符 abstract 类名{}
2、用 abstract 关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型 方法名(参数列表);//不能有方法体
3、抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
public class AbstractDetail01 {
public static void main(String[] args) {
//1、抽象类,不能被实例化
new A();//错误写法
}
}
//2、抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
abstract class A {
public void hi() {
System.out.println("hi");
}
}
//3、一旦类包含了abstract 方法,则这个类必须声明为abstract
abstract class B {
public abstract void hi();
}
//4、abstract 只能修饰类和方法,不能修饰属性和其它的
class C {
public abstract int n1 = 1; //错误写法
}
//5、如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,
除非它自己也声明为 abstract 类。
class D extens B {
@Override
public void hi() {
//这里相等于D子类实现了父类B的抽象方法,所谓实现方法,就是有方法体
}
}
//6、抽象方法不能使用 private, final 和 static 来修饰,因为这些关键字都是和重写相违背的。
abstract class E {
private abstract void hi();//错误写法
public final abstract void hi();//错误写法
public static abstract void hi();//错误写法
}
//7、抽象类的本质还是类,所以可以有类的各种成员
abstract class F {
public int n1 = 10;
public static String name = "测试";
public void hi() {
System.out.println("hi");
}
public abstract void hello();
public static void ok() {
System.out.println("ok");
}
}
//8、抽象方法不能有主体,即不能实现
abstract class G {
public abstract void hello(){//错误写法
}
}
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
模板设计模式能解决的问题
栗子🌰:
请编程实现 TestTemplate.java
abstract public class Template { //抽象类-模板设计模式
public abstract void job();//抽象方法
public void calculateTime() {//实现方法,调用job 方法
//得到开始的时间
long start = System.currentTimeMillis();
job(); //动态绑定机制
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("任务执行时间" + (end - start));
}
}
public class AA extends Template {
//计算任务1
@Override
public void job() { //实现Template 的抽象方法job
long num = 0;
for (long i = 1; i <= 800000; i++) {
num += i;
}
}
}
public class BB extends Template {
//计算任务2
@Override
public void job() {
long num = 0;
for (long i = 1; i <= 80000; i++) {
num *= i;
}
}
}
public class TestTemplate {
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime(); //多态
BB bb = new BB();
bb.calculateTime();
}
}
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来。
语法:
//定义接口
interface 接口名{
属性;//实际上是 public static final 属性;
抽象方法; //实际上是 public abstract 抽象方法;
//接口中抽象方法可以不用 abstract 关键字
}
//实现接口
class 类名 implements 接口{
自己属性;
自己方法;
必须实现的接口的抽象方法
}
public class InterfaceDetail01 {
public static void main(String[] args) {
//1.接口不能被实例化
new IA();//错误写法
}
}
//2.接口中所有的方法是public 方法, 接口中抽象方法,可以不用abstract 修饰
interface IA {
void say();//修饰符默认 public
void hi();//实际就是 public abstract void hi();
}
//3.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter 来解决
class Cat implements IA{
@Override
public void say() {
}
@Override
public void hi() {
}
}
//4.抽象类去实现接口时,可以不实现接口的抽象方法
abstract class Tiger implements IA {
}
public class InterfaceDetail02 {
public static void main(String[] args) {
//接口中的属性,是public static final
System.out.println(IB.n1);//说明n1 就是static
//IB.n1 = 30; 说明n1 是final
}
}
interface IB {
//接口中的属性,只能是final 的,而且是public static final 修饰符
int n1 = 10; //等价public static final int n1 = 10;
void hi();
}
//接口的修饰符只能是public 和默认,这点和类的修饰符是一样的
interface IC {
void say();
}
//接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB,IC {
}
//一个类同时可以实现多个接口
class Pig implements IB,IC {
@Override
public void hi() {
}
@Override
public void say() {
}
}
public class ExtendsVsInterface {
public static void main(String[] args) {
LittleMonkey wuKong = new LittleMonkey("悟空");
wuKong.climbing();
wuKong.swimming();
wuKong.flying();
}
}
//猴子
class Monkey {
private String name;
public Monkey(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void climbing() {
System.out.println(name + " 会爬树...");
}
}
//接口
interface Fishable {
void swimming();
}
interface Birdable {
void 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() + " 通过学习,可以像鸟儿一样飞翔...");
}
}
/*
悟空 会爬树...
悟空 通过学习,可以像鱼儿一样游泳...
悟空 通过学习,可以像鸟儿一样飞翔...
*/
接口和继承解决的问题不同
接口类型的变量可以指向实现了该接口类的对象实例
例如 USB 接口,既可以接收手机对象,又可以接收相机对象,就体现了接口多态(接口引用可以指向实现了接口的类的对象)
public class InterfacePolyParameter {
public static void main(String[] args) {
//接口的多态体现
//接口类型的变量usb可以指向实现了USB接口类的对象实例
USB usb = new Phone();
usb = new Camera();
//继承体现的多态
//父类类型的变量father可以指向继承Farher的子类的对象实例
Farher father = new Son();
father = new Daughter();
}
}
interface USB {}
class Phone implements USB{}
class Camera implements USB{}
class Farher {}
class Son extends Farher {}
class Daughter extends Farher {}
演示案例: 给 USB 数组中,存放 Phone 和相机对象, Phone 类还有一个特有的方法 call() ,请遍历 USB 数组,如果是 Phone 对象,除了调用 USB 接口定义的方法外,还需要调用 Phone 特有方法call
public class InterfacePolyArr {
public static void main(String[] args) {
USB[] usb = new USB[2];
usb[0] = new Phone();
usb[1] = new Camera();
for(int i = 0; i < usb.length; i++) {
usb[i].work();//动态绑定..
//需要进行类型的向下转型
if(usb[i] instanceof Phone) {//判断他的运行类型是Phone_
((Phone) usb[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("相机工作中...");
}
}
如果一个类 Teacher 实现了接口 IG ,IG 接口继承了 IH 接口,那么,实际上就相当于Teacher 类也实现了 IH 接口,这就是接口多态传递现象
public class InterfacePolyPass {
public static void main(String[] args) {
//接口类型的变量可以指向,实现了该接口的类的对象实例
IG ig = new Teacher();
//如果IG 继承了IH 接口,而Teacher 类实现了IG 接口
//那么,实际上就相当于Teacher 类也实现了IH 接口.
//这就是所谓的接口多态传递现象.
IH ih = new Teacher();
}
}
interface IH {
void hi();
}
interface IG extends IH{}
class Teacher implements IG {
@Override
public void hi() {
}
}
练习:判断代码是否有误并改进
public class InterfaceExercise02 {
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); //错误,原因不明确x
//改进
//访问接口的x就使用A.x
//访问父类的x就使用super.x
System.out.println(A.x + " " + super.x);
}
}
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
//语法
class Outer{ //外部类
class Inner{ //内部类
}
}
class Other{ //外部其他类
}
类的五大成员:[属性、方法、构造器、代码块、内部类]
定义在局部位置(方法中/代码块) :
定义在成员位置:
局部内部类是定义在外部类的局部位置(方法中或者代码块中),并且有类名。
package com.jwt.innerclass;
public class LocalInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.m1();
}
}
class Outer {//外部类
private int n1 = 100;
private int n2 = 700;
//私有方法
private void m2() {
System.out.println("外部类的私有方法 m2()");
}
//方法
public void m1() {
//1.不能添加访问修饰符,但是可以使用final 修饰
//2.作用域: 仅仅在定义它的方法或代码块中
final class Inner {//局部内部类
private int n1 = 800;//与外部类成员重名
public void f1() {
// 3.可以直接访问外部类的所有成员,包含私有的
System.out.println("n2=" + n2 );
m2();
//6. 如果外部类和局部内部类的成员重名时,
//1)默认遵循就近原则,
System.out.println("n1=" + n1 );
// 2)如果想访问外部类的成员,使用(外部类名.this.成员)去访问
System.out.println("n1=" + Outer.this.n1);
}
}
//4. 外部类访向局部内部类的成员,创建局部内部类的对象,再访问
Inner inner = new Inner();
inner.f1();
System.out.println("外部类访问局部内部类的n1=" + inner.n1);
}
}
匿名内部类是定义在外部类的局部位置(方法中或者代码块中),并且没有类名
匿名内部类的基本语法
new 类或接口(参数列表){
类体
};
匿名内部类的语法比较奇特, 匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征
package com.jwt.innerclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
}
}
class Outer05 {
private int n1 = 100;
private int n2 = 700;
public void f1() {
//1.不能添加访问修饰符
//2.作用域: 仅仅在定义它的方法或代码块中
Person p = new Person() {//创建一个基于类的匿名内部类
private int n1 = 800;//与外部类成员重名
@Override
public void hi() {
//3.可以直接访问外部类的所有成员,包含私有的
System.out.println("n2=" + n2 );
//5.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话
//1)默认遵循就近原则
System.out.println("n1=" + n1 );
//2)如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
System.out.println("n1=" + Outer05.this.n1 );
}
};
p.hi();//动态绑定
//也可以直接调用, 匿名内部类本身也是返回对象
new Person(){
@Override
public void ok(String str) {
super.ok(str);
}
}.ok("jack");
}
}
class Person {//类
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}
匿名内部类的最佳实践:当做实参直接传递,简洁高效
package com.jwt.innerclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
//当做实参直接传递,简洁高效
f1(new IL() {
@Override
public void show() {
System.out.println("这是一副名画~~...");
}
});
//传统方法
f1(new Picture());
}
//静态方法,形参是接口类型
public static void f1(IL il) {
il.show();
}
}
//接口
interface IL {
void show();
}
//类->实现IL
class Picture implements IL {
@Override
public void show() {
System.out.println("这是一副名画XX...");
}
}
练习:
1.有一个铃声接口 Bell,里面有个 ring 方法。 2.有一个手机类 Cellphone, 具有闹钟功能 alarmclock,参数是 Bell 类型 3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了 4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
package com.jwt.innerclass;
public class InnerClassExercise02 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
cellphone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
cellphone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell{
void ring();
}
class Cellphone {
public void alarmclock(Bell bell){
System.out.println(bell.getClass());
bell.ring();//动态绑定
}
}
成员内部类是定义在外部类的成员位置,并且没有 static 修饰。
//方式一:外部类名.成员内部类名 对象名 = 外部类名.new 成员内部类名()
Outer.Inner inner = Outer.new Inner()
//方式二:在外部类中编写一个方法,返回成员内部类对象
public class Inner{
...
public Inner getInnerInstance(){
return new Inner();
}
}
Outer outer = new Outer()
outer.getInnerInstance()
静态内部类是定义在外部类的成员位置,并且有 static 修饰。
//方式一:外部类名.成员内部类名 对象名 = 外部类名.new 成员内部类名()
Outer.Inner inner = new Outer.Inner()
//方式二:在外部类中编写一个方法,返回成员内部类对象
static class Inner{
...
public Inner getInnerInstance(){
return new Inner();
}
}
Outer outer = new Outer()
outer.getInnerInstance()
//或者
Outer.getInnerInstance()