写在前面
Hello大家好,我是【麟-小白】,一位软件工程专业的学生,喜好计算机知识。希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!!
如果小哥哥小姐姐们对我的文章感兴趣,请不要吝啬你们的小手,多多点赞加关注呀!❤❤❤ 爱你们!!!
目录
写在前面
1. 类的成员之四:代码块
1.1 什么是代码块
1.2 两种代码块的对比
1.3 代码块代码演示
2. 关键字:final
2.1 什么是final
2.2 final代码演示
3. 抽象类和抽象方法
3.1 什么是抽象类和抽象方法
3.2 抽象类应用
3.3 抽象类代码演示
3.4模板方法设计模式
3.5抽象类的匿名子类
3.6 关于abstract的练习
结语
【往期回顾】
一文带你深入理解【Java基础】· 面向对象编程(下)①static和main方法
一文带你深入理解【Java基础】· 面向对象编程(中)③Object类和包装类的使用
一文带你深入理解【Java基础】· 面向对象编程(中)②子类对象实例化和多态
一文带你深入理解【Java基础】· 面向对象编程(中)①继承、方法的重写、权限修饰符和super
一文带你深入理解【Java基础】· 面向对象编程(上)②重载、封装、构造器、this、package和import
一文带你深入理解【Java基础】· 面向对象编程(上)①面向对象的理解、类和对象、对象的创建使用和属性
【习题总结】
【Java基础】· 面向对象编程(下)习题详解
1. 类的成员之四:代码块
1.1 什么是代码块
代码块(或初始化块)的作用: 对Java类或对象进行初始化 代码块(或初始化块)的分类: 一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块(static block),没有使用static修饰的,为非静态代码块。static代码块通常用于初始化static的属性
class Person {public static int total;static {total = 100;// 为total赋初值}// …… 其它属性或方法声明}
1.2 两种代码块的对比
静态代码块:用static修饰的代码块1. 可以有输出语句。 2. 可以对类的属性、类的声明进行初始化操作。 3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。 4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。 5. 静态代码块的执行要先于非静态代码块。 6. 静态代码块随着类的加载而加载,且只执行一次。非静态代码块:没有static修饰的代码块1. 可以有输出语句。 2. 可以对类的属性、类的声明进行初始化操作。 3. 除了调用非静态的结构外,还可以调用静态的变量或方法。 4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。 5. 每次创建对象的时候,都会执行一次。且先于构造器执行。总结:程序中成员变量赋值的执行顺序1.3 代码块代码演示
/** 类的成员之四:代码块(或初始化块)* * 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;}// 非static的代码块{System.out.println("hello, block - 2");}{System.out.println("hello, block - 1");// 调用非静态结构age = 1;eat();// 调用静态结构desc = "我是一个爱学习的人1";info();}// static的代码块static {System.out.println("hello,static block-2");}static {System.out.println("hello,static block-1");// 调用静态结构desc = "我是一个爱学习的人";info();// 不可以调用非静态结构// eat();// name = "Tom";}// 方法public void eat() {System.out.println("吃饭");}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}public static void info() {System.out.println("我是一个快乐的人!");}}
//总结:由父及子,静态先行class Root {static {System.out.println("Root的静态初始化块");}{System.out.println("Root的普通初始化块");}public Root() {super();System.out.println("Root的无参数的构造器");}}class Mid extends Root {static {System.out.println("Mid的静态初始化块");}{System.out.println("Mid的普通初始化块");}public Mid() {super();System.out.println("Mid的无参数的构造器");}public Mid(String msg) {// 通过this调用同一类中重载的构造器this();System.out.println("Mid的带参数构造器,其参数值:" + msg);}}class Leaf extends Mid {static {System.out.println("Leaf的静态初始化块");}{System.out.println("Leaf的普通初始化块");}public Leaf() {// 通过super调用父类中有一个字符串参数的构造器super("尚硅谷");System.out.println("Leaf的构造器");}}public class LeafTest {public static void main(String[] args) {new Leaf();System.out.println();new Leaf();// Root的静态初始化块// Mid的静态初始化块// Leaf的静态初始化块// Root的普通初始化块// Root的无参数的构造器// Mid的普通初始化块// Mid的无参数的构造器// Mid的带参数构造器,其参数值:尚硅谷// Leaf的普通初始化块// Leaf的构造器//// Root的普通初始化块// Root的无参数的构造器// Mid的普通初始化块// Mid的无参数的构造器// Mid的带参数构造器,其参数值:尚硅谷// Leaf的普通初始化块// Leaf的构造器}}
lass Father {static {System.out.println("11111111111");}{System.out.println("22222222222");}public Father() {System.out.println("33333333333");}}public class Son extends Father {static {System.out.println("44444444444");}{System.out.println("55555555555");}public Son() {System.out.println("66666666666");}public static void main(String[] args) { // 由父及子 静态先行System.out.println("77777777777");System.out.println("************************");new Son();System.out.println("************************");new Son();System.out.println("************************");new Father();// 11111111111// 44444444444// 77777777777// ************************// 22222222222// 33333333333// 55555555555// 66666666666// ************************// 22222222222// 33333333333// 55555555555// 66666666666// ************************// 22222222222// 33333333333}}
2. 关键字:final
2.1 什么是final
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。final标记的类不能被继承。提高安全性,提高程序的可读性。 String类、System类、StringBuffer类final标记的方法不能被子类重写。比如:Object类中的getClass()。final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。 final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用。 final double MY_PI = 3.14;1. final修饰类
final class A {}class B extends A { //错误,不能被继承。}
中国古代,什么人不能有后代,就可以被final声明,称为“太监类”!2. final修饰方法
class A {public final void print() {System.out.println("A");}}class B extends A {public void print() { // 错误,不能被重写。System.out.println("麟-小白");}}
3. final修饰变量——常量
class A {private final String INFO = "atguigu"; // 声明常量public void print() {// The final field A.INFO cannot be assigned// INFO = "尚硅谷";}}
常量名要大写,内容不可修改。——如同古代皇帝的圣旨。 static final:全局常量
2.2 final代码演示
/** final:最终的* * 1. final可以用来修饰的结构:类、方法、变量* * 2. final 用来修饰一个类:此类不能被其他类所继承。*比如:String类、System类、StringBuffer类* * 3. final 用来修饰方法:表明此方法不可以被重写* 比如:Object类中getClass();* * 4. final 用来修饰变量:此时的"变量"就称为是一个常量*4.1 final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化* 4.2 final修饰局部变量:* 尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值* 以后,就只能在方法体内使用此形参,但不能进行重新赋值。* * static final 用来修饰属性:全局常量*/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 setDown(int down){// this.DOWN = down;// }public void doWidth() {// width = 20;}public void show() {final int NUM = 10;// 常量// NUM += 20;}public void show(final int num) {// num = 20;//编译不通过System.out.println(num);}public static void main(String[] args) {int num = 10;num = num + 5;FinalTest test = new FinalTest();// test.setDown(3);test.show(10);}}final class FinalA {}// class B extends FinalA{//// }// class C extends String{//// }class AA {public final void show() {}}class BB extends AA {// public void show(){//// }}
3. 抽象类和抽象方法
3.1 什么是抽象类和抽象方法
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。 用abstract关键字来修饰一个类,这个类叫做抽象类。 用abstract来修饰一个方法,该方法叫做抽象方法。 抽象方法:只有方法的声明,没有方法的实现。以分号结束: 比如:public abstract void talk();含有抽象方法的类必须被声明为抽象类。 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。 不能用abstract修饰变量、代码块、构造器; 不能用abstract修饰私有方法、静态方法、final的方法、final的类。3.2 抽象类应用
抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。 在航运公司系统中,Vehicle类需要定义两个方法分别计算运输工具的燃料效率和行驶距离。 问题:卡车(Truck)和驳船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同。Vehicle类不能提供计算方法,但子类可以。 解决方案 Java允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。 Vehicle是一个抽象类,有两个抽象方法。
public abstract class Vehicle {public abstract double calcFuelEfficiency(); // 计算燃料效率的抽象方法public abstract double calcTripDistance(); // 计算行驶距离的抽象方法}public class Truck extends Vehicle {public double calcFuelEfficiency() { // 写出计算卡车的燃料效率的具体方法}public double calcTripDistance() { // 写出计算卡车行驶距离的具体方法}}public class RiverBarge extends Vehicle {public double calcFuelEfficiency() { // 写出计算驳船的燃料效率的具体方法}public double calcTripDistance() { // 写出计算驳船行驶距离的具体方法}}
3.3 抽象类代码演示
/** abstract关键字的使用* 1.abstract:抽象的* 2.abstract可以用来修饰的结构:类、方法* * 3. abstract修饰类:抽象类* > 此类不能实例化*> 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)*> 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作* * * 4. abstract修饰方法:抽象方法* > 抽象方法只有方法的声明,没有方法体* > 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。*> 若子类重写了父类中的所有的抽象方法后,此子类方可实例化* 若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰*/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("人走路");}}class Student extends Person {public Student(String name, int age) {super(name, age);}public Student() {}@Overridepublic void eat() {System.out.println("学生多吃有营养的食物");}@Overridepublic void breath() {System.out.println("学生应该呼吸新鲜的没有雾霾的空气");}}
3.4模板方法设计模式
多态的应用:模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。解决的问题: 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有: 数据库访问的封装 Junit单元测试 JavaWeb的Servlet中关于doGet/doPost方法调用 Hibernate中模板程序 Spring中JDBCTemlate、HibernateTemplate等
/** 抽象类的应用:模板方法的设计模式*/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 {@Overridepublic void code() {// 求1-1000以内的素数for (int i = 2; i <= 1000; i++) {boolean isFlag = true;for (int j = 2; j <= Math.sqrt(i); j++) {if (i % j == 0) {isFlag = false;break;}}if (isFlag) {System.out.println(i);}}}}
//抽象类的应用:模板方法的设计模式public class TemplateMethodTest {public static void main(String[] args) {BankTemplateMethod btm = new DrawMoney();btm.process();// 取号排队// 我要取款!!!// 反馈评分BankTemplateMethod btm2 = new ManageMoney();btm2.process();// 取号排队// 我要理财!我这里有2000万美元!!// 反馈评分}}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万美元!!");}}
3.5抽象类的匿名子类
/** 抽象类的匿名子类* */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 abstract void eat();public void walk() {System.out.println("人走路");}}class Student extends Person {public Student(String name, int age) {super(name, age);}public Student() {}@Overridepublic void eat() {System.out.println("学生多吃有营养的食物");}@Overridepublic void breath() {System.out.println("学生应该呼吸新鲜的没有雾霾的空气");}}public class PersonTest {public static void main(String[] args) {method(new Student());//匿名对象Worker worker = new Worker();method1(worker);//非匿名的类非匿名的对象method1(new Worker());//非匿名的类匿名的对象System.out.println("********************");//创建了一匿名子类的对象:pPerson p = new Person(){@Overridepublic void eat() {System.out.println("吃东西");}@Overridepublic void breath() {System.out.println("好好呼吸");}};method1(p);System.out.println("********************");//创建匿名子类的匿名对象method1(new Person(){@Overridepublic void eat() {System.out.println("吃好吃东西");}@Overridepublic 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{@Overridepublic void eat() {}@Overridepublic void breath() {}}
3.6 关于abstract的练习
练习一:
编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。提供必要的构造器和抽象方法:work()。对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。
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();}public class Manager extends Employee {private double bonus;// 奖金public Manager(double bonus) {super();this.bonus = bonus;}public Manager(String name, int id, double salary, double bonus) {super(name, id, salary);this.bonus = bonus;}@Overridepublic void work() {System.out.println("管理员工,提高公司运行的效率");}}public class CommonEmployee extends Employee {@Overridepublic void work() {System.out.println("员工在一线车间生产产品");}}public class EmployeeTest {public static void main(String[] args) {// 多态Employee manager = new Manager("库克", 1001, 5000, 50000);manager.work();// 管理员工,提高公司运行的效率CommonEmployee commonEmployee = new CommonEmployee();commonEmployee.work();// 员工在一线车间生产产品}}
练习二:
编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元。实验说明:(1)定义一个Employee类,该类包含:private成员变量name,number,birthday,其中birthday 为MyDate类的对象;abstract方法earnings();toString()方法输出对象的name,number和birthday。 (2)MyDate类包含:private成员变量year,month,day ;toDateString()方法返回日期对应的字符串:xxxx年xx月xx日 (3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:private成员变量monthlySalary;实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输出员工类型信息及员工的name,number,birthday。 (4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:private成员变量wage和hour;实现父类的抽象方法earnings(),该方法返回wage*hour值;toString()方法输出员工类型信息及员工的name,number,birthday。 (5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。 (1)定义一个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();@Overridepublic String toString() {return "name=" + name + ", number=" + number + ", birthday=" + birthday.toDateString();}}
(2)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 + "日";}}
(3)定义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;}@Overridepublic double earnings() {return monthlySalary;}public String toString() {return "SalariedEmployee[" + super.toString() + "]";}}
(4)参照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.wage = wage;this.hour = hour;}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;}@Overridepublic double earnings() {return wage * hour;}public String toString() {return "HourlyEmployee[" + super.toString() + "]";}}
(5)定义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);//一月份:0Employee[] emps = new Employee[2];emps[0] = new SalariedEmployee("马森", 1002, new MyDate(1992, 2, 28), 10000);emps[1] = new HourlyEmployee("潘雨生", 2001, new MyDate(1991, 1, 6), 60, 240);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元");}}}}
结语
本人会持续更新文章的哦!希望大家一键三连,你们的鼓励就是作者不断更新的动力