目录
2.(补充)多态的应用:模板方法设计模式(TemplateMethod)
一、关键字:static
背景引入:有时候我们会希望无论是否产生对象或无论产生多少对象,某些特定的的数据在内存空间里只有一份。例如:当我们我们创建一个类ChinesePerson时,由于所有中国人的国籍都是China,故不必再每一个中国人的实例对象中都单独创建一个代表国家名称的变量。
1.static关键字的使用:
1.static可以用来修饰:属性、方法、代码块、内部类(注意:static不能用来修饰构造器!)
2.使用static修饰属性:静态变量(或类变量)
2.1 属性,按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
如下例:
public class StaticTest { public static void main(String[] args) { // Chinese.nation = "中国"; //可以直接通过"类.静态变量"的方法直接给其赋值 Chinese c1 = new Chinese(); c1.name = "张三"; c1.age = 40; c1.nation = "CHN"; Chinese c2 = new Chinese(); c2.name = "李四"; c2.age = 35; c1.nation = "CHINA"; System.out.println(c1.nation); //此时输出的c1.nation已被"CHINA"覆盖 //编译不通过,即不能通过类调用实例变量 // Chinese.name = "刘五"; } } class Chinese { String name; int age; static String nation; }
2.2 static修饰属性的其他说明:
① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
② 静态变量的加载要早于对象的创建。
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
④ 关于调用:
类变量 | 实例变量 | |
类 | √ | × |
对象 | √ | √ |
2.3 静态属性举例:System.out(类.out); Math.PI;
3. 使用static修饰方法:静态方法(类方法)
① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
② 关于调用:
静态方法 | 非静态方法 | |
类 | √ | × |
对象 | √ | √ |
③ 静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
4. static注意点:
4.1 在静态的方法内,不能使用this关键字、super关键字
4.2 关于静态属性和静态方法的使用,从生命周期的角度去理解
5. 开发中,如何确定一个属性是否要声明为static的?
> 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
> 类中的常量也常常声明为static
开发中,如何确定一个方法是否要声明为static的?
> 操作静态属性的方法,通常设置为static的
> 工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections
6.summary:有关static应用的代码的汇总
6.1 创建圆
public class CircleTest { public static void main(String[] args) { Circle c1 = new Circle(); Circle c2 = new Circle(); Circle c3 = new Circle(3.4); System.out.println("c1的id:" + c1.getId()); System.out.println("c2的id:" + c2.getId()); System.out.println("c3的id:" + c3.getId()); System.out.println("创建的圆的个数为:" + Circle.getTotal() ); } } class Circle{ private double radius; private int id; //自动赋值 public Circle(){ id = init++; total++; } public Circle(double radius) { this();//和删一个构造器出现相同的代码,可用this()代替 // id = init++; // total++; this.radius = radius; } private static int total; //记录创建的圆的个数 private static int init = 1001; //static声明的属性被所有对象所共享 public double findArea(){ return 3.14 * radius * radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } public int getId() { return id; } public static int getTotal() { return total; }
6.2 static最初测试最终版
public class StaticTest { public static void main(String[] args) { Chinese.nation = "中国"; Chinese c1 = new Chinese(); c1.name = "张三"; c1.age = 40; c1.nation = "CHN"; Chinese c2 = new Chinese(); c2.name = "李四"; c2.age = 35; c1.nation = "CHINA"; System.out.println(c1.nation); // Chinese.name = "刘五" //编译不通过 c1.eat(); Chinese.show(); // Chinese.eat(); //编译不通过 // Chinese.info() //如果可以调,那么输出的是谁?有歧义 } } class Chinese { String name; int age; static String nation; public void eat() { System.out.println("中国人吃中餐"); //调用非静态结构 this.info(); System.out.println("name:" + name); //调用静态结构 walk(); System.out.println("naition:" + Chinese.nation); } public static void show() { System.out.println("我是一个中国人!"); //不能调用非静态的结构 // eat(); // name = "Tom"; //可以调用静态的结构 System.out.println(Chinese.nation); walk(); } public void info() { System.out.println("name:" + name + ",age:" + age); } public static void walk() { }
6.3 数组工具类的定义及测试(加入static)
6.31 ArrayUtil的定义类
/* * 自定义数组的工具类 * */ public class ArrayUtil { // 求数组的最大值 public static int getMax(int[] arr) { int maxValue = arr[0]; for (int i = 1; i < arr.length; i++) { if (maxValue < arr[i]) { maxValue = arr[i]; } } return maxValue; } // 求数组的最小值 public static int getMin(int[] arr) { int minValue = arr[0]; for (int i = 1; i < arr.length; i++) { if (minValue > arr[i]) { minValue = arr[i]; } } return minValue; } // 求数组的总和 public static int getSum(int[] arr) { int sum = 0; for (int i = 0; i < arr.length; i++) { sum += arr[i]; } return sum; } // 求数组的平均值 public static int getAvg(int[] arr) { return getSum(arr) / arr.length; } //如下的两个同名方法构成了重载 // 反转数组 public static void reverse(int[] arr) { for (int i = 0; i < arr.length / 2; i++) { int temp = arr[i]; arr[i] = arr[arr.length - i - 1]; arr[arr.length - i - 1] = temp; } } // public static void reverse(String[] arr){ // // } // 复制数组 public static int[] copy(int[] arr) { int[] arr1 = new int[arr.length]; for (int i = 0; i < arr1.length; i++) { arr1[i] = arr[i]; } return arr1; } // 数组排序 public static void sort(int[] arr) { // 冒泡排序 for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { // int temp = arr[j]; // arr[j] = arr[j + 1]; // arr[j + 1] = temp; //错误的: // swap(arr[j],arr[j + 1]); //正确的: swap(arr,j,j + 1); } } } } //错误的:交换数组中指定两个位置元素的值 // public void swap(int i,int j){ // int temp = i; // i = j; // j = temp; // } //正确的:交换数组中指定两个位置元素的值 private static void swap(int[] arr,int i,int j){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } // 遍历数组 public static void print(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + "\t"); } System.out.println(); } // 查找指定元素 public static int getIndex(int[] arr, int dest) { // 线性查找: for (int i = 0; i < arr.length; i++) { if (dest == arr[i]) { return i; } } return -1;//返回一个负数,表示没有找到 }
6.32 ArrayUtil的测试类
public class ArrayUtilTest { public static void main(String[] args) { // ArrayUtil util = new ArrayUtil(); int[] arr = new int[]{32,34,32,5,3,54,654,-98,0,-53,5}; int max = ArrayUtil.getMax(arr); System.out.println("最大值为:" + max); System.out.println("排序前:"); ArrayUtil.print(arr); ArrayUtil.sort(arr); System.out.println("排序后:"); ArrayUtil.print(arr); // System.out.println("查找:"); // int index = util.getIndex(arr, -5); // if(index >= 0){ // System.out.println("找到了,索引地址为:" + index); // }else{ // System.out.println("未找到"); // } // util.reverse(arr); } }
6.4 编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。账号要自动生成。编写主类,使用银行账户类,输入、输出3个储户的上述信息。考虑:哪些属性可以设计成static属性。
- 6.41 Account定义类
/* * 编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”, * 定义封装这些属性的方法。账号要自动生成。 编写主类,使用银行账户类,输入、输出3个储户的上述信息。 考虑:哪些属性可以设计成static属性。 * * */ public class Account { private int id; private String passWord = "000000 "; private double balance; private static double interestRate; private static double minMoney; private static int init = 1001; public Account() { id = init++; } public Account(String passWord, double balance) { id = init++; this.passWord = passWord; this.balance = balance; } public String getPassWord() { return passWord; } public void setPassWord(String passWord) { this.passWord = passWord; } public static double getInterestRate() { return interestRate; } public static void setInterestRate(double interestRate) { Account.interestRate = interestRate; } public static double getMinMoney() { return minMoney; } public static void setMinMoney(double minMoney) { Account.minMoney = minMoney; } public int getId() { return id; } public double getBalance() { return balance; } @Override public String toString() { return "Account [id=" + id + ", passWord=" + passWord + ", balance=" + balance + "]"; } }
6.4.2 Account测试类
public class AccountTest { public static void main(String[] args) { Account acct1 = new Account(); Account acct2 = new Account("qwerty", 2000); Account.setInterestRate(0.012); Account.setMinMoney(100); System.out.println(acct1); System.out.println(acct2); System.out.println(acct1.getInterestRate()); System.out.println(acct2.getMinMoney()); } }
2.单例设计模式:
1. 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
2. 如何实现?
饿汉式 vs 懒汉式
3. 区分饿汉式 和 懒汉式
饿汉式:
坏处:对象加载时间过长。
好处:饿汉式是线程安全的
懒汉式:好处:延迟对象的创建。
目前的写法坏处:线程不安全。--->到多线程内容时,再修改
饿汉式:
public class SingletonTest1 { public static void main(String[] args) { // Bank bank1 = new Bank(); // Bank bank2 = new Bank(); //此时创建的bank1和ban2是同一个对象 Bank bank1 = Bank.getInstance(); Bank bank2 = Bank.getInstance(); System.out.println(bank1 == bank2); } } class Bank { //1.私有化类的构造器 private Bank() { } //2.内部创建类的对象 //4.要求此对象也必须声明为静态的 private static Bank instance = new Bank(); //3.提供公共的静态的方法,返回类的对象 public static Bank getInstance() { return instance; } }
一个非常特殊的情况:在final的修饰下,此代码也是单例设计模式
public class SingletonTest { public static void main(String[] args) { Bank$ bank1 = Bank$.instance; // Bank$.instance = null; Bank$ bank2 = Bank$.instance; System.out.println(bank1 == bank2); } } class Bank${ private Bank$() { } public static final Bank$ instance = new Bank$(); }
懒汉式:
/* * 单例模式的懒汉式实现 * */ public class SingletonTest2 { public static void main(String[] args) { Order order1 = Order.getInstance(); Order order2 = Order.getInstance(); System.out.println(order1 == order2); } } class Order{ //1.私有化类的构造器 private Order(){ } //2.声明当前类对象,没有初始化 //4.此对象也必须声明为static的 private static Order instance = null; //3.声明public、static的返回当前类对象的方法 public static Order getInstance(){ if(instance == null){ instance = new Order(); } return instance; } }
二、理解main方法的语法
main()方法的使用说明
1. main()方法作为程序的入口
2. main()方法也是一个普通的静态方法(调用前也要先加载类)
3. main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
再谈main方法:
public static void main(String[] args){//方法体}
>权限修饰符:private 缺省 protected public --->封装性
>修饰符:static、final、abstract、native --->可用来修饰方法
>返回值类型:无返回值、有返回值--->return
>方法名:需要满足标识符命名的规则、规范:“见名知意”
>形参列表:重载 VS 重写;参数的值传递机制;体现对象的多态性
>方法体:来体现方法的功能
Main方法:
1.1
public class MainTest { public static void main(String[] args) { Main.main(new String[100 ]); MainTest test = new MainTest(); test.show(); } public void show() { } } class Main{ public static void main(String[] args) { for (int i = 0; i < args.length; i++) { args[i] = "args_" + i; System.out.println(args[i]); } } }
1.2
public class MainDemo { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { System.out.println("*****" + args[i]); int num = Integer.parseInt(args[i]); System.out.println("#####" + num); } } }
三、类的成员之四:代码块(或初始化块)
1. 代码块的作用:用来初始化类、对象的信息
2. 代码块如果有修饰的话,只能使用static.
3. 分类:静态代码块 vs 非静态代码块
4. 静态代码块:
>内部可以有输出语句
>随着类的加载而执行,而且只执行一次
>作用:初始化类的信息
>如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
>静态代码块的执行要优先于非静态代码块的执行
>静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
5. 非静态代码块
>内部可以有输出语句
>随着对象的创建而执行
>每创建一个对象,就执行一次非静态代码块
>作用:可以在创建对象时,对对象的属性等进行初始化!
>如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
>非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
总结:对对象属性可以赋值的位置
①默认初始化
②显式初始化
③构造器初始化
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
⑤在代码块中赋值
执行的先后顺序:① - ② / ⑤ - ③ - ④
有关代码块的演示示例:
public class BlockTest { public static void main(String[] args) { String desc = Person.desc; System.out.println(desc); Person p1 = new Person(); Person p2 = new Person(); System.out.println(p1.age); Person.info(); } } class Person{ //属性 String name; int age; static String desc = "我是一个人"; //构造器 public Person(){ } public Person(String name, int age) { this.name = name; this.age = age; } //非静态代码块 { System.out.println("hello,block-1"); //调用非静态结构 age = 1; eat(); //调用静态结构 desc = "我是一个爱吃饭的人"; info(); } { System.out.println("hello,block-2"); } //静态代码块 static{ System.out.println("hello,static block1"); //调用静态结构 desc = "我是一个爱吃饭的人"; info(); //非静态结构不能调用 // eat(); // name = "Tom"; } static{ System.out.println("hello,static block2"); } //方法 public void eat() { System.out.println("吃饭"); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } public static void info() { System.out.println("我是一个静态的方法"); } }
代码块练习题:
1. 观察LeafTest语句的执行顺序(代码块的执行先于构造器!)
总结:由父及子,静态先行//总结:由父及子,静态先行 class Root{ static{ System.out.println("Root的静态初始化块");//1 } { System.out.println("Root的普通初始化块");//4 } public Root(){ super(); System.out.println("Root的无参数的构造器");//5 } } class Mid extends Root{ static{ System.out.println("Mid的静态初始化块");//2 } { System.out.println("Mid的普通初始化块");//6 } public Mid(){ super(); System.out.println("Mid的无参数的构造器");//7 } public Mid(String msg){ //通过this调用同一类中重载的构造器 this(); System.out.println("Mid的带参数构造器,其参数值:" + msg);//8 } } class Leaf extends Mid{ static{ System.out.println("Leaf的静态初始化块");//3 } { System.out.println("Leaf的普通初始化块");//9 } public Leaf(){ //通过super调用父类中有一个字符串参数的构造器 super("爪哇是java"); System.out.println("Leaf的构造器");//10 } } public class LeafTest{ public static void main(String[] args){ new Leaf(); System.out.println(); new Leaf(); //静态代码块只执行一次 } }
运行图示:
2. 观察Son类语句的执行顺序(注意main方法也是一个静态的方法)
main方法执行之前也需要通过类调用,然后就要加载类class Father { static { System.out.println("11111111111");//1 } { System.out.println("22222222222"); } public Father() { System.out.println("33333333333"); } } public class Son extends Father { static { System.out.println("44444444444");//2 } { System.out.println("55555555555"); } public Son() { System.out.println("66666666666"); } public static void main(String[] args) { // 由父及子 静态先行 System.out.println("77777777777");//3 System.out.println("************************"); new Son(); System.out.println("************************"); new Son(); System.out.println("************************"); new Father(); } }
运行图示:
四、关键字:final
1. final可以用来修饰的结构:类、方法、变量
2. final 用来修饰一个类:此类不能被其他类所继承。
比如:String类、System类、StringBuffer类
3. final 用来修饰 方法:表明此方法不可以被重写
比如:Object类中getClass();
4. final 用来修饰变量:此时的"变量"就称为是一个常量
4.1 final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
4.2 final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形赋 一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性:全局常量(大写的快捷键:Ctrl + Shift + X)
final相关代码演示示例:
package javacode; public class FinalTest { final int WIDTH = 0; final int LEFT; final int RIGHT; // final int DOWN; { LEFT = 1; } public FinalTest() { RIGHT = 2; } public FinalTest(int n) { RIGHT = n; } // public void setDwon(int down) { // this.DOWN = down; // } public void doWidth() { // width = 20; } public void show() { final int NUMBER = 10; //常量 // NUMBER += 20; } public void show(final int NUMBER) { // NUMBER = 20; System.out.println(NUMBER); } public static void main(String[] args) { int num = 10; num = num + 5; FinalTest test = new FinalTest(); // test.setDwon(3); test.show(10); } } final class FinaA{ } //class B extends FinaA{ // //} //class C extends String{ // //} //class AA{ // final public void show() { // // } //} // //class BB extends AA{ // public void show() { // // } //}
五、抽象类与抽象方法
定义:随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一
般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父
类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
1.abstract关键字的使用
1.abstract可以用来修饰的结构:类、方法
2.abstract修饰类:抽象类
>此类不能实例化
> 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
> 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
3.abstract修饰方法:抽象方法
> 抽象方法只有方法的声明,没有方法体
> 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
> 若子类重写了父类中的所有的抽象方法后,此子类方可实例化。若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
1. 一个关于抽象类的示例:
public class AbstractTest { public static void main(String[] args) { //一旦Person类抽象了,就不可实例化 // Person p1 = new Person(); // p1.eat(); } } abstract class Creature{ public abstract void breath(); } abstract class Person extends Creature{ String name; int age; public Person() { } public Person(String name, int age){ this.name = name; this.age =age; } //不是抽象方法 // public void eat() { // // } //抽象方法 public abstract void eat(); public void walk(){ System.out.println("人走路"); } } //将子类改为abstract类 class Student extends Person{ public Student(String name, int age) { super(name, age); } //1.重写 public void eat() { System.out.println("学生吃东西"); } @Override public void breath() { System.out.println("学生可以呼吸"); } }
2. 关于求几何图形面积的例子:把求面积的方法定义为abstract类,然后在子类中重写findArea方法。如下:
2.1 定义GeometricObject类public abstract class GeometricObject {//几何图形 protected String color; protected double weight; public String getColor() { return color; } public void setColor(String color) { this.color = color; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } public GeometricObject(String color, double weight) { super(); this.color = color; this.weight = weight; } public abstract double findArea(); }
2.2 定义Circle类
public class Circle extends GeometricObject { private double radius; public Circle(double radius,String color, double weight) { super(color, weight); this.radius = radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } public double findArea(){ return 3.14 * radius * radius; } }
2.3 定义MyRectangle类
public class MyRectangle extends GeometricObject { private double width; private double height; public MyRectangle(double width,double height,String color, double weight) { super(color, weight); this.width = width; this.height = height; } public double getWidth() { return width; } public void setWidth(double width) { this.width = width; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } @Override public double findArea() { return width * height; } }
2.4 定义GeometricTest测试类
/* * * 定义一个测试类GeometricTest, * 编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术), * 编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。 */ public class GeometricTest { public static void main(String[] args) { GeometricTest test = new GeometricTest(); Circle c1 = new Circle(3.3, "white", 1.0); test.displayGeometricObject(c1); Circle c2 = new Circle(3.3, "white", 1.0); test.displayGeometricObject(c2); boolean isEquals = test.equalsArea(c1, c2); System.out.println("c1 和 c2的面积是否相等:" + isEquals); MyRectangle rect = new MyRectangle(2.1, 3.4, "red", 2.0); test.displayGeometricObject(rect); } public void displayGeometricObject(GeometricObject o){//GeometricObject o = new Circle(...) System.out.println("面积为:" + o.findArea()); } //测试两个对象的面积是否相等 public boolean equalsArea(GeometricObject o1,GeometricObject o2){ return o1.findArea() == o2.findArea(); } }
练习:抽象类的应用
1. 2.2.1 Employee类的编写:
/* * 编写一个Employee类,声明为抽象类, 包含如下三个属性:name,id,salary。 提供必要的构造器和抽象方法:work()。 * */ public abstract class Employee { private String name; private int id; private double salary; public Employee() { super(); } public Employee(String name, int id, double salary) { super(); this.name = name; this.id = id; this.salary = salary; } public abstract void work(); }
2.2 Manager类的创建:
/* * 对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。 */ public class Manager extends Employee { private double bonus; public Manager() { super(); } public Manager(String name, int id, double salary, double bonus) { super(name, id, salary); this.bonus = bonus; } @Override public void work() { System.out.println("管理员工"); } }
2.3 CommonEmployee类的创建:
public class CommonEmployee extends Employee { @Override public void work() { System.out.println("员工在一线车间生产产品"); } }
2.4 EmployeeTest测试类:
/* * 请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。 */ public class EmployeeTest { public static void main(String[] args) { //多态 Employee manager = new Manager("比尔", 1001, 3000, 2000); manager.work(); CommonEmployee commonEmpoyee = new CommonEmployee(); commonEmpoyee.work(); } }
一个抽象类的匿名子类的示例:
/* * 抽象类的匿名子类 */ public class PersonTest { public static void main(String[] args) { method(new Student());// 匿名对象 Worker worker = new Worker(); method1(worker);// 非匿名类的非匿名对象 method1(new Worker());// 非匿名类的匿名对象 System.out.println("************"); // 创建一匿名子类的对象:p Person p = new Person() { @Override public void eat() { System.out.println("吃饭"); } @Override public void breath() { System.out.println("呼吸"); } }; method1(p); System.out.println("***************"); //创建匿名子类的匿名对象 method1(new Person() { @Override public void eat() { System.out.println("继续吃"); } @Override public void breath() { System.out.println("继续呼吸"); } }); } public static void method1(Person p) { p.eat(); p.breath(); } public static void method(Student s) { } } class Worker extends Person { @Override public void eat() { } @Override public void breath() { } }
2.(补充)多态的应用:模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
>当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去让子类去实现。
>换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中了但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
关于模板方法的两个示例:
1.
/* * 抽象类的应用:模板方法的设计模式 * */ public class TemplateTest { public static void main(String[] args) { SubTemplate t = new SubTemplate(); t.spendTime(); } } abstract class Template{ //计算某段代码执行需要花费的时间 public void spendTime() { long start = System.currentTimeMillis(); this.code();//不确定的部分,或者说是易变的部分 long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start)); } public abstract void code(); } class SubTemplate extends Template{ @Override public void code() { for (int i = 2; i < 1000; i++) { boolean isMark = true; for (int j = 2; j < Math.sqrt(i); j++) { if (i % j == 0) { isMark = false; break; } } if (isMark) { System.out.println(i); } } } }
2.
//抽象类的应用:模板方法的设计模式 public class TemplateMethodTest { public static void main(String[] args) { BankTemplateMethod btm = new DrawMoney(); btm.process(); BankTemplateMethod btm2 = new ManageMoney(); btm2.process(); } } abstract class BankTemplateMethod { // 具体方法 public void takeNumber() { System.out.println("取号排队"); } public abstract void transact(); // 办理具体的业务 //钩子方法 public void evaluate() { System.out.println("反馈评分"); } // 模板方法,把基本操作组合到一起,子类一般不能重写 public final void process() { this.takeNumber(); this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码 this.evaluate(); } } class DrawMoney extends BankTemplateMethod { public void transact() { System.out.println("我要取款!!!"); } } class ManageMoney extends BankTemplateMethod { public void transact() { System.out.println("我要理财!我这里有2000万美元!!"); } }
练习题:
1.1.1 Employee类的创建
* * 定义一个Employee类,该类包含: private成员变量name,number,birthday,其中birthday 为MyDate类的对象; abstract方法earnings(); toString()方法输出对象的name,number和birthday。 * */ public abstract class Employee { private String name; private int number; private MyDate birthday; public Employee(String name, int number, MyDate birthday) { super(); this.name = name; this.number = number; this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } public abstract double earnings(); @Override public String toString() { return "name=" + name + ", number=" + number + ", birthday=" + birthday.toDateString(); } }
1.2 MyDate类的创建
/* * MyDate类包含: private成员变量year,month,day ; toDateString()方法返回日期对应的字符串:xxxx年xx月xx日 */ public class MyDate { private int year; private int month; private int day; public MyDate(int year, int month, int day) { super(); this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } public String toDateString() { return year + "年" + month + "月" + day +"日"; } }
1.3 SalariedEmployee类的创建
/* * 定义SalariedEmployee类继承Employee类, * 实现按月计算工资的员工处理。该类包括:private成员变量monthlySalary; *实现父类的抽象方法earnings(),该方法返回monthlySalary值; *toString()方法输出员工类型信息及员工的name,number,birthday。 */ public class SalariedEmployee extends Employee{ private double monthlySalary; public SalariedEmployee(String name, int number, MyDate birthday) { super(name, number, birthday); } public SalariedEmployee(String name, int number, MyDate birthday, double monthlySalary) { super(name, number, birthday); this.monthlySalary = monthlySalary; } public double getMonthlySalary() { return monthlySalary; } public void setMonthlySalary(double monthlySalary) { this.monthlySalary = monthlySalary; } @Override public double earnings() { return monthlySalary; } public String toString() { return "SalariedEmployee[" + super.toString() + "]"; } }
1.4 HourlyEmployee类的创建
/* * 参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括: private成员变量wage和hour; 实现父类的抽象方法earnings(),该方法返回wage*hour值; toString()方法输出员工类型信息及员工的name,number,birthday。 */ public class HourlyEmployee extends Employee{ private int wage; private int hour; public HourlyEmployee(String name, int number, MyDate birthday) { super(name, number, birthday); } public HourlyEmployee(String name, int number, MyDate birthday, int wage, int hour) { super(name, number, birthday); this.hour = hour; this.wage = wage; } public int getWage() { return wage; } public void setWage(int wage) { this.wage = wage; } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } @Override public double earnings() { return wage * hour; } public String toString() { return "HourlyEmployee[" + super.toString() + "]"; } }
1.5 PayrollSystem测试类的创建
import java.util.Calendar; import java.util.Scanner; /* * 定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。 * 利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday。 * 当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。 */ public class PayrollSystem { public static void main(String[] args) { //方式一: // Scanner scanner = new Scanner(System.in); // System.out.println("请输入当前的月份:"); // int month = scanner.nextInt(); //方式二: Calendar calendar = Calendar.getInstance(); int month = calendar.get(Calendar.MONTH);//获取当前的月份 System.out.println(month);//九月份:8 Employee emps[] = new Employee[2]; emps[0] = new SalariedEmployee("张三", 1001, new MyDate(2001, 6, 28), 10000); emps[1] = new HourlyEmployee("李四", 1003, new MyDate(2003, 8, 25), 250, 60); for (int i = 0; i < emps.length; i++) { System.out.println(emps[i]); double salary = emps[i].earnings(); System.out.println("月工资为:" + salary); if ((month + 1) == emps[i].getBirthday().getMonth()) { System.out.println("生日快乐!奖励100元"); } } } }
3.abstract使用上的注意点:
1.abstract不能用来修饰:属性、构造器等结构
2.abstract不能用来修饰私有方法、静态方法、final的方法、final的类
努力不一定成功,但不努力一定很轻松,哈哈哈~
六、接口:interface
1.接口的使用:
1.接口使用interface来定义
2.Java中,接口和类是并列的两个结构
3.如何定义接口:定义接口中的成员
3.1 JDK7及以前:只能定义全局常量和抽象方法
>全局常量:public static final的.但是书写时,可以省略不写
>抽象方法:public abstract的.但是书写时,可以省略不写
3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
举例:
1.例子1
1.1定义CompareA接口
/* * * JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法 * */ public interface CompareA { //静态方法 public static void method1() { System.out.println("CompareA:北京"); } //默认方法 public default void method2() { System.out.println("CompareA:上海"); } default void method3() { System.out.println("CompareA:上海"); } }
1.2 定义CompareB接口
public interface CompareB { default void method3() { System.out.println("CompareB:上海"); } }
1.3 定义SuperClass类
public class SuperClass { public void method3() { System.out.println("SuperClass:北京"); }; }
1.1定义SubClassTest测试类
public class SubClassTest { public static void main(String[] args) { SubClass s = new SubClass(); // s.method1(); // SubClass.method1(); // 知识点1:接口中定义的静态方法,只能通过接口来调用。 CompareA.method1(); // 知识点2:通过实现类的对象,可以调用接口中的默认方法。 // 如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法 s.method2(); // 知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法, // 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则 // 知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法, // 那么在实现类没有重写此方法的情况下,报错。-->接口冲突。 // 这就需要我们必须在实现类中重写此方法 s.method3(); // SubClass.method2(); } } class SubClass extends SuperClass implements CompareA, CompareB { public void method2() { System.out.println("SubClass:上海"); } public void method3() { System.out.println("SuperClass:深圳"); } //知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法 public void myMethod() { method3();//调用自己定义的重写的方法 super.method3();//调用的是父类中声明的 //调用接口中的默认方法 CompareA.super.method3(); CompareB.super.method3(); } }
2.例子2
interface Filial {// 孝顺的 default void help() { System.out.println("老妈,我来救你了"); } } interface Spoony {// 痴情的 default void help() { System.out.println("媳妇,别怕,我来了"); } } class Father{ public void help(){ System.out.println("儿子,就我媳妇!"); } } class Man extends Father implements Filial, Spoony { @Override public void help() { System.out.println("我该就谁呢?"); Filial.super.help(); Spoony.super.help(); } }
4.接口中不能定义构造器!意味着接口不可以实例化
5.Java开发中,接口通过让类去实现(implements)的方式来使用.
如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
6.Java类可以实现多个接口 --->弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE
7.接口与接口之间可以继承,而且可以多继承
8.接口的具体使用,体现多态性
9.接口,实际上可以看做是一种规范
interface的简单一个简单示例:
public class InterfaceTest { public static void main(String[] args) { System.out.println(Flyable.MAX_SPEED); System.out.println(Flyable.MIN_SPEED); // Flyable.MIN_SPEED = 2; Plane plane = new Plane(); plane.fly(); } } interface Flyable { // 全局常量 public static final int MAX_SPEED = 7900; int MIN_SPEED = 1; // 省略了public static final // 抽象方法 public abstract void fly(); void stop();// 省略了public abstract // public Flyable() { // // } } interface Attackable{ void attack(); } class Plane implements Flyable { @Override public void fly() { System.out.println("飞机通过引擎起飞"); } @Override public void stop() { System.out.println("驾驶员停止"); } } abstract class Kite implements Flyable{ @Override public void fly() { // TODO Auto-generated method stub } } class Bullet extends Object implements Flyable, Attackable, CC{ @Override public void attack() { // TODO Auto-generated method stub } @Override public void fly() { // TODO Auto-generated method stub } @Override public void stop() { // TODO Auto-generated method stub } @Override public void method1() { // TODO Auto-generated method stub } @Override public void method2() { // TODO Auto-generated method stub } } //******************************************** interface AA{ void method1(); } interface BB{ void method2(); } interface CC extends AA, BB{ // void method3(); }
面试题:抽象类与接口有哪些异同?
相同点:不能实例化;都可以包含抽象方法
不同点:1)把抽象类和接口(jdk7、jdk8、jdk9)的定义、内部结构解释说明一下
2)类:单继承性 接口:多继承
类与接口:多实现
2.接口的使用(续):
1.接口使用上也满足多态性
2.接口使用上也满足多态性
3.开发中,体会面向接口编程!
一个关于接口的示例:
public class USBTest { public static void main(String[] args) { Computer com = new Computer(); //1.创建了接口的非匿名实现类的非匿名对象 Flash flash = new Flash(); com.transferData(flash); // 2.创建了接口的非匿名实现类的匿名对象 com.transferData(new Printer()); // 3.创建了接口的匿名实现类的非匿名对象 USB phone = new USB() { @Override public void start() { System.out.println("手机开始工作"); } @Override public void stop() { System.out.println("手机结束工作"); } }; com.transferData(phone); // 4.创建了接口的匿名实现类的非匿名对象 com.transferData(new USB() { @Override public void stop() { System.out.println("mp3停止工作"); } @Override public void start() { System.out.println("mp3开始工作"); } }); } } class Computer{ public void transferData(USB usb) { usb.start(); System.out.println("具体传输数据的细节"); usb.stop(); } } interface USB{ //常量:定义了长、宽、最大最小的传输速度 void start(); void stop(); } class Flash implements USB{ @Override public void start() { System.out.println("U盘开启工作"); } @Override public void stop() { System.out.println("U盘结束工作"); } } class Printer implements USB{ @Override public void start() { System.out.println("打印机开始工作"); } @Override public void stop() { System.out.println("打印机结束工作"); } }
代理模式的链各个示例:
1.
/* * 接口的应用:代理模式 * */ public class NetWorkTest { public static void main(String[] args) { Server server = new Server(); // server.browse(); ProxyServer proxyServer = new ProxyServer(server); proxyServer.browse(); } } interface NetWork { public void browse(); } //被代理类 class Server implements NetWork { @Override public void browse() { System.out.println("真实的服务器访问网络"); } } //代理类 class ProxyServer implements NetWork{ private NetWork work; public ProxyServer(NetWork work) { this.work = work; } public void check() { System.out.println("联网之前的检查工作"); } @Override public void browse() { check(); work.browse(); } }
2.
public class StaticProxyTest { public static void main(String[] args) { Proxy s = new Proxy(new RealStar()); s.confer(); s.signContract(); s.bookTicket(); s.sing(); s.collectMoney(); } } interface Star { void confer();// 面谈 void signContract();// 签合同 void bookTicket();// 订票 void sing();// 唱歌 void collectMoney();// 收钱 } //被代理类 class RealStar implements Star { public void confer() { } public void signContract() { } public void bookTicket() { } public void sing() { System.out.println("明星:歌唱~~~"); } public void collectMoney() { } } //代理类 class Proxy implements Star { private Star real; public Proxy(Star real) { this.real = real; } public void confer() { System.out.println("经纪人面谈"); } public void signContract() { System.out.println("经纪人签合同"); } public void bookTicket() { System.out.println("经纪人订票"); } public void sing() { real.sing(); } public void collectMoney() { System.out.println("经纪人收钱"); } }
改错题:
1.
interface A { int x = 0; } class B { int x = 1; } class C extends B implements A { public void pX() { //编译不通过。因为x是不明确的 // System.out.println(x); System.out.println(super.x);//1 System.out.println(A.x);//0 } public static void main(String[] args) { new C().pX(); } }
2.
七、类的成员之五:内部类
1.类的内部成员之五:内部类
1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
3.成员内部类:
一方面,作为外部类的成员:
>调用外部类的结构
>可以被static修饰
>可以被4种不同的权限修饰
另一方面,作为一个类:
> 类内可以定义属性、方法、构造器等
> 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
> 可以被abstract修饰
4.关注如下的3个问题:
4.1 如何实例化成员内部类的对象
4.2 如何在成员内部类中区分调用外部类的结构
4.3 开发中局部内部类的使用 见代码《InnerClassTest1.java》
代码演示示例:
1.
public class InnerClassTest { public static void main(String[] args) { //创建Dog实例(静态的成员内部类) Person.Dog dog = new Person.Dog(); dog.show(); //创建Bird实例(非静态的成员内部类) // Person.Bird bird = new Person.Bird();//错误的 Person p = new Person(); Person.Bird bird = p.new Bird(); bird.sing(); System.out.println(); bird.display("黄鹂"); } } class Person { String name = "小明"; int age; public void eat() { System.out.println("人吃饭饭"); } // 静态成员内部类 static class Dog { String name; int age; public void show() { System.out.println("我不是八公"); // eat(); } } // 非静态成员内部类 class Bird { String name = "杜鹃"; public Bird() { Person.this.eat();// 调用外部类的属性 } public void sing() { System.out.println("我是一只小小鸟"); // eat(); System.out.println(age); } public void display(String name) { System.out.println(name);//方法的形参 System.out.println(this.name);//内部类的属性 System.out.println(Person.this.name);//外部类的属性 } } public void method() { // 局部内部类 class AA { } } { // 局部内部类 class BB { } } public Person() { // 局部内部类 class CC { } } }
2.InnerClassTest1
public class InnerClassTest1 { //开发中很少见 public void method() { //局部内部类 class AA{ } } //返回一个实现了Comparable接口的类的对象 public Comparable getComparable(){ //创建了一个实现了Comparable接口的类:局部内部类 //方式一: // class MyComparable implements Comparable{ // // @Override // public int compareTo(Object o) { // // TODO Auto-generated method stub // return 0; // } // // } // // return new MyComparable(); //方式二: return new Comparable() { @Override public int compareTo(Object o) { // TODO Auto-generated method stub return 0; } }; } }
3.关于内部类的补充
public class InnerClassTest { public void onCreate() { } /* * 在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话, * 要求此局部变量声明为final的。 * * jdk 7及之前版本:要求此局部变量显式的声明为final的 * jdk 8及之后的版本:可以省略final的声明 * */ public void method() { //局部变量 int num = 10; class AA{ public void show() { // num = 20; System.out.println(num); } } } }