阶段一
多态
1、属性重写问题
属性没有重写之说,属性的值看编译类型!
instanceOf(比较操作符):用于判断对象的运行类型是否为XX类型、XX类型的子类型(PolyDetail03.java)
注:instanceOf:用于检测左边的对象运行类型是否为右边对象的类型或者右边对象类型的子类型,返回true
Eg.儿子 instanceOf 儿子
儿子 instenceOf 爹
爹 instanceOf 爹
2、向上转型
前提:两个对象存在继承关系
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名 = new 子类类型();
- 特点: 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(需遵循访问权限)
- 不能调用子类中的特有成员
- 最终运行效果看子类的具体实现,即调用方式,按照从运行类型开始查找方法,然后调用,规则和前面的继承调用方法规则一致
- 在编译阶段,能调用哪些成员,是由编译类型来决定的
- 程序在通过编译后,进入运行阶段,能调用哪些方法,看运行类型
注意:方法看运行类型,属性(定义的变量)看编译类型
3、向下转型
- 语法:子类类型 引用名 = (子类类型)父类引用
- 只能强制转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
- 向下转型:父类的引用对象希望可以调用运行类型的子类的特有方法
4、动态绑定机制(重点)
(DynamicBinding.java com.zyj.Poly.dynamic)
- 当调用对象方法的时候,该方法会和对象的内存地址(运行类型)绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
5、多态应用
5.1、多态数组
(PloyArray.java com.zyj.Poly.Polyarr)
- 多态数组:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
- 应用实例:现有一个继承结构如下:要求创建一个Person对象,2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象say方法
Person对象
Teacher对象
Student对象
3.应用升级:如何调用子类特有方法,比如Teacher有一个teach,Student有一个study怎么调用?
Teacher的特有方法:
Student的特有方法:
运行结果:
5.2、多态参数
方法定义的参数类型为父类类型,实参类型允许为子类类型
应用实例:(PloyParameter.java com.zyj.Poly.PolyParameter)
- 定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法。普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工多了work方法,普通员工和经理类要求分别重写getAnnual方法
- 测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
- 测试类中添加一个方法,textWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
Object类
(com.zyj.Oject Equals01.java)
1、==运算符
==和equals的对比【面试题】
-
==:既可以判断基本类型,又可以判断引用类型
-
==:如果可以判断基本类型,判断的是值是否相等
-
==:如果判断引用类型,判断的是地址是否相等
2、equals方法
-
是Object类中的方法,只能判断引用类型
-
object中默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。
Eg. Integer的源码(其中的equals方法被重写)
注:其中的value为传入的值
3、如何重写equals方法
(EqualsExercise01.java)
应用实例:判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之false
4、hashCode方法
6个小结
- 提高具有哈希结构的容器效率
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的,不能完全将哈希值等价于地址
- 案例演示(HashCode.java)
- 后面在集合,中hashCode如果需要的话,也会重写
5、toString方法
- 基本介绍:全类名(包名+类名) + @+哈希值的十六进制,【查看Object的toString方法】子类往往重写toString方法,用于返回对象的属性
/*Object的toString源码
(1)getClass().getName():类的全类名(包名+类名)
(2)Integer.toHexString(hashCode()):将对象的HashCode值转成16进制的字符串*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
-
重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式(ToString.java)
-
直接输出一个对象时,toString方法会被默认的调用,比如:sout(Monster);就会默认调用monster.toString()
6、finalize方法
(Finalize.java)
- 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作。
- 什么时候被回收:当某个对象没有任何作用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
- 垃圾回收机制的调用,是由系统来决定的,也可以通过System.gc()主动出发垃圾回收机制,测试:Car[name]
断点调试(Debug)
-
实际需求:
- 在开发中,可以利用断点调试查找程序中的错误。
- 重要调试:在断点调试过程中,是运行状态,是以对象的运行类型来执行的
-
介绍:
- 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码即显示错误,停下。进行分析从而找到这个Bug
- 断电调试是程序员必须掌握的技能
- 断电调试也能帮助我们查看Java底层源代码的执行过程
-
快捷键:F7:跳入
shift+F8:跳出
F9:执行到下一个断点
F7:跳入方法内
F8:逐行执行代码
零钱通项目
(SmallChangeSys.java)
课后练习
Exercise01
定义一个Person类{name,age,job},初始化Person对象数组,有3个person对象,并按照age从大到小进行排序,提示,使用冒泡排序(Homework01.java)
package com.zyj.Object;
public class Homework01 {
public static void main(String[] args) {
Personp[] p = new Personp[3];
p[0] = new Personp("jjj",10,"javase工程师");
p[1] = new Personp("zz",20,"大数据分析师");
p[2] = new Personp("y",30,"嵌入式");
//错误一: int temp = 0;
Personp temp = null;
for (int i = 0;i<p.length-1;i++){
for (int j = 0;j<p.length-1-i;j++){
if (p[j].getAge()<p[j+1].getAge()){
temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
}
}//错误二: sout(p[i]);
}
System.out.println("按照age从大到小排序后的效果:");
for (int i = 0;i<p.length;i++){
System.out.println(p[i]);
}
//按照名字长度从小到大排序
//错误三: String temp2 = null;
Personp temp2 = null;
for (int i = 0;i<p.length-1;i++){
for (int j = 0;j< p.length-1-i;j++){
/*错误四:
temp2 = p[j].getName();
p[j].getName() = p[j+1].getName();
p[j+1].getName() = temp2;*/
//错误五: if (p[j].getName()<p[j+1].getName()) {
if (p[j].getName().length()>p[j+1].getName().length()){
temp2 = p[j];
p[j] = p[j + 1];
p[j + 1] = temp2;
}
}
}
System.out.println("按照name从小到大排序后的效果:");
for (int i = 0;i<p.length;i++){
System.out.println(p[i]);
}
}
}
/*定义一个Person类型{name,age,job},初始化Person 对象数组,有3个person对象并按照age从大到小进行排序,提示,使用冒泡排序*/
class Personp {
private String name;
private int age;
private String job;
public Personp(String name, int age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}
输出结果:
Exercise02
写出四中访问修饰符和各自的访问权限
本类 同包 子类 不同包
public 可以 可以 可以 可以
protected 可以 可以 可以 不可
默认 可以 可以 不可 不可
private 可以 不可 不可 不可
Exercise03
(Homework03.java)
编写老师类
-
要求属性"名字name" “年龄age” “职称post” “基本工资salary”
-
编写业务方法,introduce() 实现一个教师的信息
-
编写教师类的三个子类:教授类(Professor)、副教授类、讲师类。工资级别分为:教授为1.3、副教授1.2、讲师为1.1、在三个子类里面都重写父类的introduce() 方法
-
定义并初始化一个老师对象,调用业务方法,实现对象基本信息的打印
package com.zyj.Exercise;
public class Homework03 {
public static void main(String[] args) {
Professor professor = new Professor("贾宝玉",30,"高级职称",30000.0,1.3);
professor.introduce();
}
}
class Teacher{
private String name;
private int age;
private String post;
private double salary;
private double grade;
//工资级别
public double getGrade() {
return grade;
}
public void setGrade(double grade) {
this.grade = grade;
}
public void introduce(){
System.out.println( "Teacher{" +
"age=" + age +
", post='" + post + '\'' +
", salary=" + salary +
", grade=" + grade +
'}');
}
public Teacher(String name, int age, String post, double salary, double grade) {
this.age = age;
this.post = post;
this.salary = salary;
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPost() {
return post;
}
public void setPost(String post) {
this.post = post;
}
public double getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
class Professor extends Teacher{
public Professor(String name,int age, String post, double salary, double grade) {
super(name,age, post, salary, grade);
}
public void introduce(){
super.introduce();
}
}
结果显示:
Exercise04
(Homework04.java)
通过继承实现员工工资核算打印功能
父类:员工类
子类:部门经理、普通员工类
- 部门经理工资=1000+单日工资X天数X等级(1.2)
- 普通员工工资=单日工资X天数X等级(1.0)
- 员工属性:姓名,单日工资,工作天数
- 员工方法:打印工资
- 普遍员工及部门经理都是员工子类,需要重写打印工资方法
- 定义并初始化普通员工对象,调用打印工资方法输入工资,定义初始化部门经理对象,调用打印工资方法输入工资
package com.zyj.Exercise;
public class Homework04 {
public static void main(String[] args) {
employee e = new employee("aaa",65,31,1.0);
e.Salary();
}
}
class employee{
private String name;
private double DaySalary;
private double workDay;
private double level;
//打印工资
public void Salary(){
//错误一: System.out.println(employee.getName() + "工资为:");
System.out.println(name + "工资为:\n" + DaySalary*workDay*level);
}
public employee(String name, double daySalary, double workDay, double level) {
this.name = name;
DaySalary = daySalary;
this.workDay = workDay;
this.level = level;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getDaySalary() {
return DaySalary;
}
public void setDaySalary(double daySalary) {
DaySalary = daySalary;
}
public double getWorkDay() {
return workDay;
}
public void setWorkDay(double workDay) {
this.workDay = workDay;
}
public double getLevel() {
return level;
}
public void setLevel(double level) {
this.level = level;
}
@Override
public String toString() {
return "employee{" +
"name='" + name + '\'' +
", DaySalary='" + DaySalary + '\'' +
", workDay='" + workDay + '\'' +
", level='" + level + '\'' +
'}';
}
}
class Staff extends employee{
public Staff(String name, double daySalary, double workDay, double level) {
super(name, daySalary, workDay, level);
}
@Override
public void Salary(){
super.Salary();
}
}
class Manage extends employee{
//错误二:private int bons = 1000;
private double bonus;
public Manage(String name, double daySalary, double workDay, double level) {
super(name, daySalary, workDay, level);
}
public void Salary(){
//错误三:super.Salary()+getBonus();
System.out.println(bonus+getDaySalary()*getWorkDay()*getLevel());
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
}
运行结果:
Exercise05
房屋租赁系统
模式设计:
注:Domain:数据层
Utils.Utility:
工具类的作用:
处理各种情况的用户输入,并且能够按照程序员的需求,得到用户的控制台输入。
import java.util.*;
*/
public class Utility {
//静态属性。。。
private static Scanner scanner = new Scanner(System.in);
/**
* 功能:读取键盘输入的一个菜单选项,值:1——5的范围
* @return 1——5
*/
public static char readMenuSelection() {
char c;
for (; ; ) {
String str = readKeyBoard(1, false);//包含一个字符的字符串
c = str.charAt(0);//将字符串转换成字符char类型
if (c != '1' && c != '2' &&
c != '3' && c != '4' && c != '5') {
System.out.print("选择错误,请重新输入:");
} else break;
}
return c;
}
/**
* 功能:读取键盘输入的一个字符
* @return 一个字符
*/
public static char readChar() {
String str = readKeyBoard(1, false);//就是一个字符
return str.charAt(0);
}
/**
* 功能:读取键盘输入的一个字符,如果直接按回车,则返回指定的默认值;否则返回输入的那个字符
* @param defaultValue 指定的默认值
* @return 默认值或输入的字符
*/
public static char readChar(char defaultValue) {
String str = readKeyBoard(1, true);//要么是空字符串,要么是一个字符
return (str.length() == 0) ? defaultValue : str.charAt(0);
}
/**
* 功能:读取键盘输入的整型,长度小于2位
* @return 整数
*/
public static int readInt() {
int n;
for (; ; ) {
String str = readKeyBoard(10, false);//一个整数,长度<=10位
try {
n = Integer.parseInt(str);//将字符串转换成整数
break;
} catch (NumberFormatException e) {
System.out.print("数字输入错误,请重新输入:");
}
}
return n;
}
/**
* 功能:读取键盘输入的 整数或默认值,如果直接回车,则返回默认值,否则返回输入的整数
* @param defaultValue 指定的默认值
* @return 整数或默认值
*/
public static int readInt(int defaultValue) {
int n;
for (; ; ) {
String str = readKeyBoard(10, true);
if (str.equals("")) {
return defaultValue;
}
//异常处理...
try {
n = Integer.parseInt(str);
break;
} catch (NumberFormatException e) {
System.out.print("数字输入错误,请重新输入:");
}
}
return n;
}
/**
* 功能:读取键盘输入的指定长度的字符串
* @param limit 限制的长度
* @return 指定长度的字符串
*/
public static String readString(int limit) {
return readKeyBoard(limit, false);
}
/**
* 功能:读取键盘输入的指定长度的字符串或默认值,如果直接回车,返回默认值,否则返回字符串
* @param limit 限制的长度
* @param defaultValue 指定的默认值
* @return 指定长度的字符串
*/
public static String readString(int limit, String defaultValue) {
String str = readKeyBoard(limit, true);
return str.equals("")? defaultValue : str;
}
/**
* 功能:读取键盘输入的确认选项,Y或N
* 将小的功能,封装到一个方法中.
* @return Y或N
*/
public static char readConfirmSelection() {
System.out.println("请输入你的选择(Y/N): 请小心选择");
char c;
for (; ; ) {//无限循环
//在这里,将接受到字符,转成了大写字母
//y => Y n=>N
String str = readKeyBoard(1, false).toUpperCase();
c = str.charAt(0);
if (c == 'Y' || c == 'N') {
break;
} else {
System.out.print("选择错误,请重新输入:");
}
}
return c;
}
/**
* 功能: 读取一个字符串
* @param limit 读取的长度
* @param blankReturn 如果为true ,表示 可以读空字符串。
* 如果为false表示 不能读空字符串。
*
* 如果输入为空,或者输入大于limit的长度,就会提示重新输入。
* @return
*/
private static String readKeyBoard(int limit, boolean blankReturn) {
//定义了字符串
String line = "";
//scanner.hasNextLine() 判断有没有下一行
while (scanner.hasNextLine()) {
line = scanner.nextLine();//读取这一行
//如果line.length=0, 即用户没有输入任何内容,直接回车
if (line.length() == 0) {
if (blankReturn) return line;//如果blankReturn=true,可以返回空串
else continue; //如果blankReturn=false,不接受空串,必须输入内容
}
//如果用户输入的内容大于了 limit,就提示重写输入
//如果用户如的内容 >0 <= limit ,我就接受
if (line.length() < 1 || line.length() > limit) {
System.out.print("输入长度(不能大于" + limit + ")错误,请重新输入:");
continue;
}
break;
}
return line;
}
}
Domain.House:
package com.zyj.HouseRentSys.Domain;
/*House:表示一个房屋信息*/
public class House {
//属性:编号 电话 地址 月租 状态(未出租/已出租)
private int id;
private String name;
private String num;
private String address;
private int rent;
private String state;
public House(int id, String name, String num, String address, int rent, String state) {
this.id = id;
this.name = name;
this.num = num;
this.address = address;
this.rent = rent;
this.state = state;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public House() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getRent() {
return rent;
}
public void setRent(int rent) {
this.rent = rent;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
@Override
public String toString() {
return id +
"\t\t"+ name +
"\t"+ num +
"\t\t" + address +
"\t" + rent +
"\t" + state ;
}
}
Service.HouseService
package com.zyj.HouseRentSys.Service;
import com.zyj.HouseRentSys.Domain.House;
/*
* HouseService.java 类【业务层】
* 定义House【】,保存House对象
* 1.响应HouseView的调用
* 2.完成对房屋信息的各种操作(增删改查crud)
* */
public class HouseService {
private House[] houses;//保存House对象
private int houseNums = 1;
private int idCounter = 1;
//构造器 输入House对象
public HouseService(int size){
houses = new House[size];//当创建HouseServic对象,指定数组
//为了配合测试,这里初始化一个House对象
houses[0] = new House(1,"jack","112","海淀区",2000,"未出租");
}
//add方法,添加新对象,返回boolean
public boolean add(House newHouse){
if(houseNums == houses.length){
System.out.println("数组已满,不能再添加.......");
return false;
}
//把newHouse对象加入到,新增加了一个房屋,后并自增
houses[houseNums++] = newHouse;
//我们程序员需要设计一个id自增长的机制,然后更新newHouse的id
newHouse.setId(++idCounter);
return true;
}
//del方法 删除一个房屋信息
public boolean del(int delId){
//应该先找到要删除的房屋信息对应二下标
int index = -1;
for (int i = 0; i< houseNums; i++){
if (delId == houses[i].getId()){
index = i;//就使用index
}
}
if (index == -1){//说明delId在数组中不存在
return false;
}
for (int i = index;i<houseNums-1;i++){
houses[i] = houses[i+1];
}
houses[--houseNums] = null;//把当前存有的房屋信息的最后一个,设置null
return true;
}
//根据id查找房屋信息的功能
public House findById(int findId){
for (int i = 0;i<houseNums;i++){
if (findId == houses[i].getId()){
return houses[i];
}
}
return null;
}
//list方法 返回house
public House[] list(){
return houses;
}
}
View.HouseView
```java
package com.zyj.HouseRentSys.View;
import com.zyj.HouseRentSys.Domain.House;
import com.zyj.HouseRentSys.Service.HouseService;
import com.zyj.HouseRentSys.Utils.Utility;
/*
* 1.显示界面
* 2.接收用户的输入
* 3.调用HouseService完成对房屋信息的各种操作
* */
public class HouseView {
private boolean loop = true;//控制显示菜单
private char key =' ';//接收用户选择
private HouseService houseService = new HouseService(10);//设置数组的大小
//编写listHouse()显示房屋列表
public void ListHouse(){
System.out.println("================房 屋 列 表=================");
System.out.println("编号\t\t姓名\t\t电话\t\t地址\t\t月租\t\t状态(未出租/已出租)");
House[] houses = houseService.list();//得到所有房屋信息
for (int i = 0;i< houses.length;i++){
System.out.println(houses[i]);
if (houses[i] == null){//如果为null,就不用显示了
break;
}
}
System.out.print("================房屋列表显示完毕=================");
}
//编写addHouse()向房屋列表添加数据
public void AddHouse(){
System.out.println("================添 加 房 屋 列 表=================");
System.out.println("姓名:");
String name = Utility.readString(8);
System.out.println("电话:");
String num = Utility.readString(12);
System.out.println("地址:");
String address = Utility.readString(16);
System.out.println("月租:");
int rent = Utility.readInt();
System.out.println("状态:");
String state = Utility.readString(3);
//创建一个新的House对象,注意id,是系统分配的
House house = new House(0,name,num,address,rent,state);
if (houseService.add(house)){
System.out.println("================添加房屋成功=================");
}else {
System.out.println("================添加房屋失败=================");
}
}
//编写delHouse() 接收输入的id 调用Service 的del方法
public void delHouse(){
System.out.println("================删 除 房 屋 信 息=================");
System.out.println("请输入待删除房屋的编号(-1退出):");
int delId = Utility.readInt();
if (delId == -1){
System.out.println("================放 弃 删 除 房 屋 信 息=================");
return;
}
//注意该方法本身就有循化判断的逻辑,必须输出Y/N
char choice = Utility.readConfirmSelection();
if (choice == 'Y'){
if (houseService.del(delId)){
System.out.println("================删 除 房 屋 信 息 成 功=================");
}else {
System.out.println("================房屋编号不存在,删除失败=================");
}
}else {
System.out.println("================放 弃 删 除 房 屋 信 息=================");
}
}
//完成退出确认
public void exit(){
//这里使用Utility提供的方法,完成确认
char c = Utility.readConfirmSelection();
if (c == 'Y'){
loop = false;
}
}
//根据id查找房屋信息
public void findHouse(){
System.out.println("================查 找 房 屋 信 息=================");
System.out.println("请输入要查找的id");
int findId = Utility.readInt();
//调用方法 得到返回的数值定义为house
House house = houseService.findById(findId);
if (house !=null){
System.out.println(house);
}else {
System.out.println("================没 有 查 找 到 房 屋 信 息================");
}
}
//修改房屋信息
public void updateHouse(){
System.out.println("================修 改 房 屋 信 息=================");
System.out.println("请输入要修改的id(输入-1表示退出)");
int alterId = Utility.readInt();
if (alterId == -1){
System.out.println("================放 弃 修 改 房 屋 信 息=================");
return;
}
//根据输入的alterId,查找对象
House house = houseService.findById(alterId);//返回的是引用类型,即就是HouseService中定义的数组元素
if (house == null){
System.out.println("================没 有 查 找 到 房 屋 信 息================");
return;
}
System.out.println("姓名(" + house.getName() + ":)");
String name = Utility.readString(8,"");//这里如果用户直接回车表示不修改该信息,默认是一个空字符串
if (!"".equals(name)){//如果输入的不是空字符串,就进行修改
house.setName(name);//使用house(在HouseService.java 108行中有定义)调用setName方法的好处是,可以直接修改HouseService数组中的数据
}
System.out.println("电话(" + house.getNum() + ":)");
String number = Utility.readString(12,"");
if (!"".equals(number)){
house.setNum(number);
}
System.out.println("地址(" + house.getAddress() + ":)");
String address = Utility.readString(18,"");
if (!"".equals(address)){
house.setAddress(address);
}
System.out.println("租金(" + house.getRent() + ":)");
int rent = Utility.readInt(-1);
if (rent != -1){
house.setRent(rent);
}
System.out.println("状态(" + house.getState() + ":)");
String state = Utility.readString(3,"");
if (!"".equals(state)){
house.setState(state);
}
System.out.println("================修 改 房 屋 信 息 成 功================");
}
//显示主菜单
public void mainMenu(){
do {
System.out.println("================房屋租赁系统=================");
System.out.println("\t\t\t1.新 增 房 源\t\t\t");
System.out.println("\t\t\t2.查 找 房 源\t\t\t");
System.out.println("\t\t\t3.删 除 房 屋 信 息\t\t\t");
System.out.println("\t\t\t4.修 改 房 屋 信 息\t\t\t");
System.out.println("\t\t\t5.房 屋 列 表\t\t\t");
System.out.println("\t\t\t6.退 出 系 统\t\t\t");
System.out.println("请输入你的选择(1-6):");
key = Utility.readChar();
switch (key){
case '1':
AddHouse();
break;
case '2':
findHouse();
break;
case '3':
delHouse();
break;
case '4':
updateHouse();
break;
case '5':
ListHouse();
break;
case '6':
exit();
loop = false;
break;
}
}while (loop);
}
}
**HouseRentApp**
package com.zyj.HouseRentSys;
import com.zyj.HouseRentSys.Service.HouseService;
import com.zyj.HouseRentSys.View.HouseView;
public class HouseRentApp {
public static void main(String[] args) {
//创建HouseView对象,并且显示主菜单,是整个程序的入口
new HouseView().mainMenu();
System.out.println("================你退出了系统~=================");
new HouseView().ListHouse();
}
}
第二阶段
p376_类变量内存剖析
类变量的内存布局:
小结:1.static变量,在同一个类中所有的对象共享2.static变量,是在类加载的时候,就生成了。
类什么时候被加载?
①创建对象实例时(new)
②创建子类对象实例,父类也会被加载
③使用类的静态成员时(静态属性,静态方法)
参考资料:
CSDN作者:N3verL4nd
知乎作者:ETIN
p392_单例模式
构造器私有化:防止直接new
类的内部创建对象
向外提供一个静态的公共方法
代码实现----饿汉式
package com.zyj.single_;
public class SingleTon01 {
public static void main(String[] args) {
//直接通过方法可以获取对象
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
}
}
class GirlFriend{
private String name;
//为了能够在静态方法中,返回gf对象,需要将其修饰为静态的
private static GirlFriend gf = new GirlFriend("小花花");
//如何保障我们只创建一个GrilFriend对象?
//1、将构造器私有化
//2、在类的内部直接创建,该对象是静态的
//3、提供一个公共的静态方法,返回gf对象
//为什么称这个为饿汉式?
//因为在类的内部直接创建的静态对象(private static GirlFriend gf = new GirlFriend("小花花");),随只要类加载所创建的静态对象也随之加载被创建
//,即还没有使用到gf对象时,gf对象已经随着类的加载提前创建完成了
private GirlFriend(String name) {
this.name = name;
}
//希望在没有创建对象的时候,调用方法GirFriend所以创建一个公共的静态方法,用于返回gf对象
public static GirlFriend getInstance(){
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
代码实现-----懒汉式
package com.zyj.single_;
public class SingleTon02 {
}
class Cat{
private String name;
private static Cat cat ;
//懒汉式
//步骤:
//1.仍然将构造器私有化
//2.定义一个静态的私有属对象
//3.提供一个public的static方法,可以返回一个Cat对象
//4.懒汉式,只有当用户使用getInstance时,才返回cat对象,后面再次调用时,会返回上次创建的cat的对象。从而保证了单例
private 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 + '\'' +
'}';
}
}
小结——饿汉式VS懒汉式
二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实力。而懒汉式是在使用时才创建
饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会进一步完善)
饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题
在我们Javase标准类中,Java.lang.Runtime就是经典的单例模式
P405_java接口应用场景
现在有一个项目经理,管理三个程序员,功能开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现
实现要求:3个程序员,编写三个类,分别完成对Mysql、Oracle、DB2数据库的链接connect、close…(interface03.java)
P403_接口快速入门
Interface01.java
package org.example.Interface_;
public class Interface01 {
public static void main(String[] args) {
//创建手机,相机对象
//Camera 实现了 UsbInterface
Camera camera = new Camera();
Phone phone = new Phone();
//创建计算机
Computer computer = new Computer();
computer.work(phone);//把手机接入到计算机
System.out.println("============");
computer.work(camera);//把相机接入计算机
}
}
Camera.java
package org.example.Interface_;
public class Camera implements UsbInterface{
@Override
public void start() {
System.out.println("相机start");
}
@Override
public void stop() {
System.out.println("相机stop");
}
}
Computer.java
package org.example.Interface_;
public class Computer {
//编写一个方法:计算机工作
//1.UsbInterface usbInterface 形参是接口类型 UsbInterface
//2.看到 接受 实现了UsbInterface接口的类的对象实例
public void work(UsbInterface usbInterface) {
//通过接口,来调用方法
usbInterface.start();
usbInterface.stop();
}
}
Phone.java
package org.example.Interface_;
//Phone 类 实现UsbInterface
//1.Phone类需要实现 UsbInterface接口 规定/声明的方法
public class Phone implements UsbInterface{
@Override
public void stop() {
System.out.println("stop");
}
@Override
public void start() {
System.out.println("start");
}
}
UsbInterface.java
package org.example.Interface_;
//Phone 类 实现UsbInterface
//1.Phone类需要实现 UsbInterface接口 规定/声明的方法
public class Phone implements UsbInterface{
@Override
public void stop() {
System.out.println("stop");
}
@Override
public void start() {
System.out.println("start");
}
}
P406_java接口使用细节1
接口不能被实例化
接口中所有方法是public方法,接口中抽象方法,可以不用abstract修饰
将所有的方法实现,可以使用alt+enter来快速实现
抽象类实现接口,可以不用实现接口的抽象方法
p407_Java接口使用细节2
(InterfaceDetail02.java)
一个类同时可以实现多个接口
接口中的属性,只能是final的,而且public static final 修饰符
比如:int a = 1,实际上:public static int a = 1;(必须初始化)
接口中属性的访问形式:接口名.属性名
一个接口不能继承其他的类,但是可以继承多个别的接口
例.interface A extends B,C{}
接口的修饰符 只能是public 和 默认,这点和类的修饰符一样
p408_接口课堂练习
interface A{int a = 23;}
class B implements A{}
main中
B b = new B();
sout(b.a);
sout(A.a);
sout(B.a);
语法是否正确?输出什么?
正确 23 23 23
p409_接口VS继承
(ExtendsVsInterface.java)
实现这种机制,是对Java单继承机制的补充!
接口和继承解决的问题不同:
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其他类去实现这些方法
接口比继承更灵活:
接口比继承更加灵活,继承是解决is-a的关系(Animal-Cat),而接口只需满足like-a的关系(Fishable-Cat)
接口在一定程度上实现代码解藕(即,接口规范性+动态绑定机制)
package com.zyj.interface_;
public class ExtendsVsInterface {
public static void main(String[] args) {
LittleMonkey wuKong = new LittleMonkey("悟空");
wuKong.climbing();
wuKong.swimming();
}
}
//猴子类
class Monkey{
private String name;
public void climbing(){
System.out.println(name + "猴子会爬树");
}
public Monkey(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//小猴子类
//小猴子类
//小结:当子类继承了父类,就自动的拥有了父类的功能
//如果子类需要扩展功能,可以通过实现接口的方式扩展
//可以简单的理解为:实现接口是对Java单继承机制的补充
class LittleMonkey extends Monkey implements Fishable{
public LittleMonkey(String name) {
super(name);
}
@Override
public void swimming() {
System.out.println(getName() + "通过学习,可以像鱼一样游泳");
}
}
//接口
interface Fishable{
void swimming();
}
p410_接口的多态特性
1.多态参数(前面案例体现)InterfacePolyParameter.java
在前面的Usb接口案例,UsbInterface usb ,既可以接受手机对象,又可以接受相机对象,就体现了 接口 多态(接口引用可以指向实现了接口类的对象)
package org.example.Interface_;
public class InterfacePolyParameter {
public static void main(String[] args) {
//接口的多态体现
//接口类型的变量 if01 可以指向 实现了IF接口类的对象实例
IF if01 = new Moneter();
if01 = new Car();
//继承体现的多态
//父类类型的变量a 可以指向 继承AAA的子类的对象实例
AAA a = new BBB();
a = new CCC();
}
}
interface IF{}
class Moneter implements IF{}
class Car implements IF{}
class AAA{}
class BBB extends AAA{}
class CCC extends AAA{}
2.多态数组 InterfaceArr.java
演示案例:给Usb数组中,存放Phone和相机对象,Phone类还有一个特有的方法call(),请便利Usb数组,如果是Phone对象,除了调用Usb接口定义的方法除外,还需要调用Phone特有方法call
package org.example.Interface_;
public class InterfacePolyArr {
public static void main(String[] args) {
//多态数组->接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone_();
usbs[1] = new Camera_();
/*
* 给Usb数组中,存放Phone和相机对象,Phone类还有一个特有的方法call(),请便利Usb数组,
* 如果是Phone对象,除了调用Usb接口定义的方法除外,还需要调用Phone特有方法call*/
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("相机工作中");
}
}
3.接口存在多态传递现象 InterfacePolyPass.java
package org.example.Interface_;
/*
* 演示多态传递现象*/
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() {
}
}
p413_四种内部类(重点)
1.基本介绍:一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套其他类的类称为外部类。是我们类的第五大成员【思考类的五大成员是哪些?属性、构造器、方法、方法体、代码块】,内部类最大的特点就是可以直接访问私有属性,并且可以体现按类与类之间的包含关系
2.基本语法:
class Outer{ //外部类
class Inner{ //内部类
}
}
class Other{//外部其他类
}
//InnerClass01.java
3.内部类的分类:
a.定义在外部类局部位置上(比如方法内):
局部内部类(有类名)
匿名内部类(没有类名,!!重点!!)
b.定义在外部类的成员位置上:
成员内部类(没用static修饰)
静态内部类(使用static修饰)
p414_局部内部类1
(LocalInnerClass.java)
局部内部类的使用
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
1、可以直接访问外部类的所有成员,包含私有的
2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
3、作用域:仅仅在定义它的方法或代码块中
4、局部内部类–访问–>外部类的成员【访问方式:直接访问】
5、外部类–访问–>局部内部类的成员
访问方式:创建对象,再访问(注意:必须在作用域内)
记住:a.局部内部类定义在方法中/代码块
b.作用域在方法体或者代码块中
c.本质仍然是一个类
package org.example.innerclass;
import org.springframework.http.converter.json.GsonBuilderUtils;
/*演示局部内部类的使用*/
public class LocalInnerClass {
public static void main(String[] args) {
//演示一遍
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02{//外部类
private int n1 = 100;
private void m2(){
System.out.println("Outer02 m2()");
}//私有方法
public void m1(){//方法
//1、局部内部类是定义在外部类的局部位置,通常在方法中
//3、不能添加访问修饰符,因为它的地位就是一个局部变量;
// 局部变量是不能使用修饰符的。但是可以使用final修饰,
// 因为局部变量也可以使用final。
//4、作用域:仅仅在定义它的方法或代码块中
final class Inner02{//局部内部类(本质仍然是一个类)
//2、可以直接访问外部类的所有成员,包含私有的
private int n1 = 800;
public void f1(){
//5、局部内部类可以直接访问外部类的成员,比如下面:访问了外部类的n1和 m2
//7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,
// 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问【演示】。
//解释“如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问”:
//即,本质就是外部类的对象,即哪个对象调用了m1,Outer02.this就是哪个对象
System.out.println("n1=" + n1 + "外部类的n1=" + Outer02.this.n1);//调用的是外部类的n1
System.out.println("n1=" + n1 + "内部类的n1=" + this.n1);//调用的是内部类的n1
m2();
}
}
//6、外部类在方法中,可以创建Inner02对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
p415_局部内部类2
6、外部其他类–不能访问–>局部内部类(因为局部内部类地位是一个局部变量)
7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问【演示】
package org.example.innerclass;
import org.springframework.http.converter.json.GsonBuilderUtils;
/*演示局部内部类的使用*/
public class LocalInnerClass {
public static void main(String[] args) {
//演示一遍
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02{//外部类
private int n1 = 100;
private void m2(){
System.out.println("Outer02 m2()");
}//私有方法
public void m1(){//方法
//1、局部内部类是定义在外部类的局部位置,通常在方法中
//3、不能添加访问修饰符,因为它的地位就是一个局部变量;
// 局部变量是不能使用修饰符的。但是可以使用final修饰,
// 因为局部变量也可以使用final。
//4、作用域:仅仅在定义它的方法或代码块中
final class Inner02{//局部内部类(本质仍然是一个类)
//2、可以直接访问外部类的所有成员,包含私有的
private int n1 = 800;
public void f1(){
//5、局部内部类可以直接访问外部类的成员,比如下面:访问了外部类的n1和 m2
//7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,
// 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问【演示】。
//解释“如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问”:
//即,本质就是外部类的对象,即哪个对象调用了m1,Outer02.this就是哪个对象
System.out.println("n1=" + n1 + "外部类的n1=" + Outer02.this.n1);//调用的是外部类的n1
System.out.println("n1=" + n1 + "内部类的n1=" + this.n1);//调用的是内部类的n1
m2();
}
}
//6、外部类在方法中,可以创建Inner02对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
p416_匿名内部类本质
(AnonymousInnerClass.java)
a.本质是类
b.内部类
c.该类没有名字
d.同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如说方法中,并且没有类名
1.匿名内部类的基本语法:
new 类或接口(参数列表){
类体
};
1)基于接口的匿名内部类
- 匿名内部类只使用一次
- new IA{};:在jdk的底层创建了匿名内部类Outer04$1,并创建了Outer04$1的实例,并且把地址返回给tiger
- IA tiger = new IA(){};:tiger的运行类型:匿名内部类
- IA tiger = new IA(){};:tiger的编译类型:IA
package com.zyj.innerclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04{//外部类
private int n1 = 10;//属性
public void method(){//方法
//基于接口的匿名内部类
//1.需求:想使用接口IA,并创建对象
//2.传统方式,写一个类,实现该接口,并创建对象
//3.需求是Tiger/Dog类只是使用依次,后面不再使用
//4.解决的方法:使用匿名内部类
//tiger的编译类型?IA
//tiger的运行类型?匿名内部类
/*看底层:XXX==外部类名$1
* class XXX implements IA{
* @Override
public void cry() {
System.out.println("。。。");
}
* }*/
//5.jdk底层在创建匿名内部类Outer04$1,立即创建了Outer04$1的实例,并且把地址返还给tiger
//6.匿名内部类使用一次就没有了
IA tiger = new IA(){
@Override
public void cry() {
System.out.println("。。。");
}
};
System.out.println(tiger.getClass());//得到XXX
tiger.cry();
}
}
interface IA{//接口
public void cry();
}
class Father{//类
public Father(String name){//构造器
}
public void test(){//方法
}
}
p417_匿名内部类使用
2)基于类的匿名内部类
package com.zyj.innerclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04{//外部类
private int n1 = 10;//属性
public void method(){//方法
//基于接口的匿名内部类
//1.需求:想使用接口IA,并创建对象
//2.传统方式,写一个类,实现该接口,并创建对象
//3.需求是Tiger/Dog类只是使用依次,后面不再使用
//4.解决的方法:使用匿名内部类
//tiger的编译类型?IA
//tiger的运行类型?匿名内部类
/*看底层:XXX==外部类名$1
* class XXX implements IA{
* @Override
public void cry() {
System.out.println("。。。");
}
* }*/
//5.jdk底层在创建匿名内部类Outer04$1,立即创建了Outer04$1的实例,并且把地址返还给tiger
//6.匿名内部类使用一次就没有了
IA tiger = new IA(){
@Override
public void cry() {
System.out.println("。。。");
}
};
System.out.println(tiger.getClass());//得到XXX
tiger.cry();
//基于类的内部类
//1.father编译类型 Father
//2.father运行类型 匿名内部类名
//3.注意("jack") 参数列表会传送给构造器
Father father = new Father("jack"){
};
//基于抽象类的匿名内部类
Animal animal = new Animal(){
@Override
void eat() {
System.out.println("eat...");
}
};
animal.eat();
}
}
interface IA{//接口
public void cry();
}
class Father{//类
public Father(String name){//构造器
System.out.println("name" + name);
}
public void test(){//方法
}
}
abstract class Animal{//抽象类
abstract void eat();
}
p418匿名内部类细节
(AnonymousInnerClassDetail.java)
2.匿名内部类的语法比较奇特,请大家注意,因为匿名内部类即是一个类的定义,同时它本身也是一个对象,因此从语法上看,它即有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
package com.zyj.innerclass;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
}
}
class Outer05{
private int n1 = 99;
public void f1(){
//创建一个基于类的匿名内部类
//1.也可以直接调用,匿名内部类本身也是返回对象
//2.class 匿名内部类 extends Person{},所以super还是指代的Person
new Person(){
@Override
public void hi() {
System.out.println("匿名");
}
@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("ok");
}
}
3.可以直接访问外部类的所有成员,包含私有的
4.不能添加访问修饰符,因为它的地位就是一个局部变量
5.作用域:仅仅在定义它的方法或代码块中
6.匿名内部类–>访问–>外部类成员
7.外部其它类–>不能访问–>匿名内部类(因为 匿名内部类地位是一个局部变量)
8.如果外部类和内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
package com.zyj.innerclass;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
}
}
class Outer05{
private int n1 = 99;
public void f1(){
//创建一个基于类的匿名内部类
//1.也可以直接调用,匿名内部类本身也是返回对象
//2.class 匿名内部类 extends Person{},所以super还是指代的Person
new Person(){
@Override
public void hi() {
//3.可以直接访问外部类的所有成员,包含私有的
System.out.println("匿名"+n1);
}
@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("ok");
}
}
p419_匿名内部类实践
(InnerClassExercise01)
匿名内部类的最佳实践:当做实参直接传递,简洁高效
package com.zyj.innerclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
//当做实参直接传递,高效
f1(new AA() {
@Override
public void show() {
System.out.println("这是一幅名画");
}
});
//传统方法
f1(new Picture());
}
//静态方法,形参是接口类型
public static void f1(AA aa){
aa.show();
}
}
interface AA{
void show();
}
//传统方法:实现接口
class Picture implements AA{
@Override
public void show() {
System.out.println("传统方法实现");
}
}
课堂练习:
如何通过匿名内部类实现接口?方法如下
1.有一个铃声接口Bell,里面有个ring方法
2.有一个手机类CellPhone,具有闹铃功能alarmclock,参数是Bell类型
3.测试手机类的闹铃功能,通过匿名内部类(对象)作为参事,打印:aaa
4.再传入另一个匿名内部类(对象),打印bbb
package com.zyj.innerclass;
public class InnerClassExercise02 {
public static void main(String[] args) {
CallPhone cp = new CallPhone();
//1.传递的是实现了Bell接口的匿名内部类
//2.重写了ring
//3.Bell bell = new Bell() {
// @Override
// public void ring() {
// System.out.println("aaaa");
// }
// }
cp.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("aaaa");
}
});
cp.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("bbbbb");
}
});
}
}
interface Bell {
void ring();
}
class CallPhone{
public void alarmClock(Bell bell){//形参是Bell接口的类型
bell.ring();
}
}
解析以上代码:
此范例实现了通过匿名内部类实现接口。
首先,代码中定义了一个Bell接口,该接口实现了一个ring方法。并定义了一个名为CallPhone方法,定义了一个alarmClock方法,该方法的形参为接口Bell接口类型的参数。在此方法内部传入了Bell对象的ring()方法.
在main()方法中,首先创建了一个CallPhone类型的对象cp。然后通过匿名内部类的方式创建了两个Bell接口的实例,并实现了每个实例的ring方法。
此范例中,在方法中直接定义内部类的方法实现。通过这种方法,可以避免建立额外的类来实现接口,使代码更简洁,且可以根据需要自定义其方法的实现。
p420_成员内部类1
成员内部类的使用:
(MemberInnerClass.java)
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰
1.可以直接访问外部类的所有成员,包含私有的
2.可以添加任意的访问修饰符(public private protected 默认 )因为它的地位就是一个成员
package com.zyj.innerclass;
public class MemberInnerClass {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
}
}
class Outer08{
private int n1 = 10;
public String name = "张三";
class Inner08{//成员内部类
//1.因为这个内部类,没有写在方法中,也没有写在代码块中,而是直接放在Outer08的成员的位置上。所以叫成员内部类
//2.可以添加任意访问修饰符(public protected 默认 private),因为它的地位就是一个成员
public void say(){
//可以直接访问外部类的所有成员,包含私有的
System.out.println("外部类的n1" + n1 + "外部类的name" + name);
}
}
//写方法
public void t1(){
//使用成员内部类
Inner08 inner08 = new Inner08();
inner08.say();
}
}
p421_成员内部类02
(MemberInnerClass02.java)
3.作用域和外部其它成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法
4.成员内部类–>访问–>外部类成员(比如:属性)【访问方式:直接访问】(说明)
5.外部类–>访问–>成员内部类【访问方式:创建对象,再访问】
6.外部其他类–>访问–>成员内部类
7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
package com.zyj.innerclass;
public class MemberInnerClass02 {
public static void main(String[] args) {
Outer080 outer080 = new Outer080();
outer080.t1();
//外部其他类使用成员内部类的三种方式
//1.outer080.new Inner080();相当于把 new Inner090()当做是outer080成员
Outer080.Inner080 inner080 = outer080.new Inner080();
inner080.say();
//2.在外部类中,编写一个方法,可以返回Inner080对象
Outer080.Inner080 innter080Instance = outer080.getInnter080Instance();
innter080Instance.say();
//3.综合前两种
new Outer080().new Inner080();
}
}
class Outer080{
private int n1 = 10;
public String name = "张三";
class Inner080{//成员内部类
//1.因为这个内部类,没有写在方法中,也没有写在代码块中,而是直接放在Outer08的成员的位置上。所以叫成员内部类
//2.可以添加任意访问修饰符(public protected 默认 private),因为它的地位就是一个成员
private int n2 = 10;
private int n1 = 99;
public void say(){
//可以直接访问外部类的所有成员,包含私有的
//如果成员内部类的成员和外部类的成员重名,会遵循就近原则
//,可以通过 外部类名.this.属性 来访问外部类的成员
System.out.println("外部类的n1" + n1 + "外部类的name" + name + "外部类的n1" + Outer080.this.n1);
}
}
public Inner080 getInnter080Instance(){
return new Inner080();
}
//写方法
public void t1(){
//使用成员内部类
//创建成员内部类的对象,然后使用相关方法
Inner080 inner080 = new Inner080();
inner080.say();
System.out.println(inner080.n2);
}
}
p422_静态内部类1
(StaicInnerClass.java)
静态内部类的使用:
说明:静态内部类是定义在外部类的成员位置,并且有static修饰
1.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2.可以添加任意访问修饰符,因为它的地位是一个成员
3.作用域:同其他成员,为整个类体
package com.zyj.innerclass;
public class StaicInnerClass {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
}
}
class Outer10{//外部类
private int n1 = 10;
private static String name = "张三";
//静态内部类:
//1.放在外部类的成员位置
//2.使用static 修饰
//3.可以访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
//4.可以添加任意的访问修饰符,因为他的地位是一个成员
//5.作用域:同其他的成员,为整个类体
static class Inner10{
public void say(){
System.out.println(name);
}
}
public void m1(){
Inner10 inner10 = new Inner10();
inner10.say();
}
}
p423_静态内部类2
(StaicInnerClass.java)
4.静态内部类–>访问–>外部类(比如:静态属性)【访问方式:直接访问所有静态成员】
5.外部类–>访问–>静态内部类【访问方式:创建对象,再访问】
6.外部其他类–>访问–>静态内部类(三种方法)
7.如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
package com.zyj.innerclass;
public class StaicInnerClass {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类,访问静态内部类三种方法:
//方式一 通过类名访问静态内部类,因为静态可以直接通过类名访问,不用先创建外部类对象,前提是满足访问权限
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式二 写一个方法,返回一个静态内部类的对象实例
Outer10.Inner10 inner102 = outer10.getInner10();
inner102.say();
//方法三 创建一个静态方法,返回一个静态内部类的对象实例 优点:不用创建外部类的对象
//调用:外部类.静态类名
Outer10.Inner10 inner101 = Outer10.getInner10_();
inner101.say();
}
}
class Outer10{//外部类
private int n1 = 10;
private static String name = "张三";
//静态内部类:
//1.放在外部类的成员位置
//2.使用static 修饰
//3.可以访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
//4.可以添加任意的访问修饰符,因为他的地位是一个成员
//5.作用域:同其他的成员,为整个类体
static class Inner10{
public void say(){
System.out.println(name);
}
}
public void m1(){
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10(){
return new Inner10();
}
public static Inner10 getInner10_(){
return new Inner10();
}
}
小结:
1.内部类有四种:局部内部类、匿名内部类、成员内部类、静态内部类
2.重点还是掌握匿名内部类的使用
new 接口/类 (参数列表){};
3.局部内部类,静态内部类是放在外部类的成员位置,本质就是一个成员
p425_枚举类引入
自动生成带有自己名字的注解:File—Settings—Editor—File and Code Templates—Includes—File Header
枚举类:把具体的对象一个一个例举出来的类
创建Season对象有如下特点(分析问题)
1.季节的值是有限的几个值(spring,summer,autumn,winter)
2.只读,不需要修改
解决方案-枚举:
1.枚举是一组常量的集合
2.枚举属于一种特殊的类,里面只包含了一组有限的特定的对象
p426_自定义枚举类(enum)
枚举的两种实现方式:
1.自定义类实现枚举
2.使用enum,关键字实现枚举
自定义类实现枚举-应用案例
(Enumeration02.java)
1.不需要提供setXxx方法,因为枚举对象值通常为只读
2.对枚举对象/属性使用final+static 共同修饰,实现底层优化
3.枚举对象名通常使用全部大写,常量的命名规范
4.枚举对象根据需要,也可以有多个属性
package com.zyj.Enum;
public class Enumeration02 {
public static void main(String[] args) {
System.out.println(Season.AUTUMN);
}
}
//演示自定义枚举实现
class Season{
private String name;
private String desc;
//使用static,目的是使可以通过类名直接调用.但是用static的弊端为,类会被加载----优化----->添加final可以避免类的加载
public static Season SPRING = new Season("春天","温暖");
public static Season WINTER = new Season("冬天","寒冷");
public static Season AUTUMN = new Season("秋天","凉爽");
public static Season SUMMER = new Season("夏天","炎热");
//1.将构造器私有化,目的防止,直接new
//2.去掉set相关方法,目的防止属性被修改
//3.在Season内部,直接创建固定的对象
//4.可以增加final修饰符
private Season(String name,String desc){
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
关于为何对于自定义枚举实现要使用final+static 方式?
因为在使用类的静态成员时(静态属性,静态方法),类会被加载。
也就是说,只是用static时,类会被加载,资源会被浪费。
但 final+static 搭配使用,不会导致类加载,底层编译器做了优化处理。
小结:进行自定义类实现枚举,有如下特点:
1.构造器私有化
2.本类内部创建一组对象
3.对外暴露对象(通过为对象添加public static final 修饰符)
4.可以提供get方法,但是不能提供set
p427_enum枚举类1
enum关键字实现枚举类-快速入门
(Enumeration03.java)
使用enum来实现前面的枚举案例
如果使用enum关键字来实现枚举类
1.使用关键字enum来替代 class
2.public static Season SPRING = new Season(“春天”,“温暖”);直接使用 SPRING(“春天”,“温暖”);替代
3.SPRING(“春天”,“温暖”);解读 常量名(实参列表)实参列表参考构造器的参数
4.如果有多个常量(对象),使用逗号间隔即可
5.如果使用enum来实现枚举,要求将定义的常量对象,写在最前面
package com.zyj.Enum;
public class Enumeration03 {
public static void main(String[] args) {
System.out.println(Season.AUTUMN);
}
}
//演示使用enum关键字来实现枚举类
enum Season2{
//使用static,目的是使可以通过类名直接调用.但是用static的弊端为,类会被加载----优化----->添加final可以避免类的加载
/* public static Season SPRING = new Season("春天","温暖");
public static Season WINTER = new Season("冬天","寒冷");
public static Season AUTUMN = new Season("秋天","凉爽");
public static Season SUMMER = new Season("夏天","炎热");
*/
//如果使用enum关键字来实现枚举类
//1.使用关键字enum来替代 class
//2.public static Season SPRING = new Season("春天","温暖");直接使用 SPRING("春天","温暖");替代
//3.SPRING("春天","温暖");解读 常量名(实参列表)实参列表参考构造器的参数
//4.如果有多个常量(对象),使用逗号间隔即可
//5.如果使用enum来实现枚举,要求将定义的常量对象,写在最前面
SPRING("春天","温暖"),WINTER("冬天","寒冷"),AUTUMN("秋天","凉爽");
private String name;
private String desc;
//1.将构造器私有化,目的防止,直接new
//2.去掉set相关方法,目的防止属性被修改
//3.在Season内部,直接创建固定的对象
//4.可以增加final修饰符
private Season2(String name,String desc){
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season2{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
p428_enum枚举类2
对于使用enum关键字实现枚举注意事项的总结:
1.当我们使用enum关键字开发一个枚举类时,默认会继承Enum类,而且是一个final类
如何进行证明默认会继承Enum类?使用javap工具(反编译工具)
2.传统的public static final Season2 SPRING =new Season2(“春天”,“温暖”);
简化为 SPRING(“春天”,“温暖”);。这里必须知道,它调用的是哪个构造器
3.如果使用无参构造器 创建 枚举对象 ,则实参列表和小括号都可以省略
4.当有多个枚举对象时,使用,间隔最后一个使用;进行结尾
5.枚举对象必须放在枚举类的行首
6.如果使用的是无参构造器,创建常量对象,则可以省略()
p429_enum关键字实现枚举-课堂练习
enum Gender{
BOY,GIRL;
}
以上代码是否正确?正确
因为,BOY,GIRL; 这里其实调用的是Gender类的无参构造器。在Gender中没有写其他的构造器,所以默认的无参构造器是存在的。
enum Gender2{
BOY,GIRL;
}
psvm{
Gender2 boy = Gender2.BOY;
Gender2.boy2 = Gender2.BOY;
sout(boy);
sout(boy2 == boy);
}
代码解析:
1.Gender2 boy = Gender2.BOY;
Gender2.boy2 = Gender2.BOY;:这两句代码以为将常量对象BOY,分别付给了boy、boy2
2.sout(boy);:本质是调用Gender2的父类的toString(),这是因为没有重写父类的toString方法,所以输出的时候自动调用 toString方法。这里的父类的toString方法指的是Enum的toString方法,因为只要使用了enum关键字实现枚举,都会默认继承 Java.lang.Enum
3.父类中的toString方法指的是
public String toString(){
return name;
}
4.由于父类的toString方法返回的是常量的名字,则boy输出结果为BOY,boy2输出结果为BOY
以上代码输出什么?
BOY true
p430_Enum成员方法
enum常用方法说明:
使用关键字enum时,会隐式继承Enum类,这样我们就可以使用Enum类相关的方法
enum常用方法应用实例
EnumMethod.java
package com.zyj.Enum;
/*演示Enum类的各种方法的使用*/
public class EnumMethod {
public static void main(String[] args) {
/*使用Enumeration03.Season2 枚举类,来演示各种方法*/
Season2 autumn = Season2.AUTUMN;
//输出枚举对象的名字
System.out.println(autumn.name());
//输出该枚举对象的次序,即编号(从0开始编号)
System.out.println(autumn.ordinal());
//从反编译可以看出 values方法,返回 Season2[]
//含有定义的所有枚举对象
Season2[] values = Season2.values();
//增强for循环:快捷方式 变量名.for
//依次从 values 数组中取出数据 付给value。如果取出完成,退出for
for (Season2 value : values) {
System.out.println(value);
}
//valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
//执行流程:
//1.根据输入的"AUTUMN"到Season2的枚举对象去查找
//2.如果找到了,就返回。如果没有找到,就报错
Season2 autumn1 = Season2.valueOf("AUTUMN");
System.out.println("autumn1" + autumn1);
//compareTo:比较两个枚举常量,比较的就是编号
//实际上
/*public final int compareTo(E o) {
//相当于 Season2.AUTUMN的编号 - Season2.WINTER的编号
return self.ordinal - other.ordinal;
}*/
System.out.println(Season2.AUTUMN.compareTo(Season2.WINTER));
}
}
p431_Enum课堂练习
package com.zyj.Enum;
public class EnumExercise02 {
public static void main(String[] args) {
/*1.声明Week枚举类,其中包含星期一至星期日的定义;MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY
* 2.使用values返回所有的枚举数组,并遍历,实现效果*/
week[] values = week.values();
System.out.println("===所有星期的信息如下===");
for (week value : values) {
System.out.println(value);
}
}
}
enum week{
MONDAY ("星期一"),TUESDAY ("星期二"),WEDNESDAY ("星期三"),
THURSDAY ("星期四"),FRIDAY ("星期五") ,SATURDAY ("星期六"),
SUNDAY ("星期天");
private String name;
week(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return name + '\t';
}
}
p432_enum使用细节
EnumDetail.java
1.使用enum关键字后,就不能再继承其他类了,因为enum会隐式继承Enum,而Java为单继承机制
2.枚举类和普通类一样,可以实现接口,这是因为enmu实现的枚举类,仍然是一个类,所以可以实现接口。如下形式:
enum 类名 implements 接口1,接口2{}
p433_Override注解
1.注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息
2.和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入的代码中的补充信息
3.在java se,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在javaee中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替Java EE九班中所有遗留的繁冗代码和XML配置等
基本的Annotation介绍
使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素
三个基本的Annotation:
1.@Override:限定某个方法,是重写父类方法(从编译层面上),该注解只能用于方法
如果写了此注解,编译器就会去检察该方法是否真的重写了父类的方法,如果的确重写了,则编译通过,如果没有构成重 写,则编译错误
此注解在底层为@interface类型,@interface表示为注解类型,并不是接口类型
2.@Deprecated:用于表示某个程序元素(类、方法等)已过时,即不推荐使用,但还是使用
此注解可以做到新旧版本的兼容过渡
p434_Deprecated注解
3.@SuppressWaring:抑制编译器警告
可以在{“”}中写入希望抑制的(不显示)警告信息
Eg.@SuppressWarnings({“all”}):抑制所有警告
作用范围:和放置的位置有关,通常可以放在具体的语句上,或者方法,又或者为类上
可以指定的警告类型有
all,抑制所有警告
boxing,抑制与封装/拆装作业相关的警告
cast,抑制与强制转型作业相关的警告
dep-ann,抑制与淘汰注释相关的警告
deprecation,抑制与淘汰的相关警告
fallthrough,抑制与 switch 陈述式中遗漏 break 相关的警告
finally,抑制与未传回 finally 区块相关的警告
hiding,抑制与隐藏变数的区域变数相关的警告
incomplete-switch,抑制与 switch 陈述式(enum case)中遗漏项目相关的警告
javadoc,抑制与 javadoc 相关的警告
nls,抑制与非 nls 字串文字相关的警告
null,抑制与空值分析相关的警告
rawtypes,抑制与使用 raw 类型相关的警告
resource,抑制与使用 Closeable 类型的资源相关的警告
restriction,抑制与使用不建议或禁止参照相关的警告
serial,抑制与可序列化的类别遗漏 serialVersionUID 栏位相关的警告
static-access,抑制与静态存取不正确相关的警告
static-method,抑制与可能宣告为 static 的方法相关的警告
super,抑制与置换方法相关但不含 super 呼叫的警告
synthetic-access,抑制与内部类别的存取未最佳化相关的警告
sync-override,抑制因为置换同步方法而遗漏同步化的警告
unchecked,抑制与未检查的作业相关的警告
unqualified-field-access,抑制与栏位存取不合格相关的警告
unused,抑制与未用的程式码及停用的程式码相关的警告
4.@Target是修饰注解的注解,称为元注解
p436_四种元注解
元注解的基本介绍:
jdk的元Annotation用于修饰其他的Annotation
元注解:本身作用不大,但在源码中有所出现
源码的种类(了解):
1.Retention//指定注解的作用范围,三种SOURCE、CLASS、RUNTIME
2.Target//指定注解可以在哪些地方使用
3.Documented//指定该注解是否在javadoc体现
4.Inherited//子类会继承父类注解
p437_作业1
执行结果:9 red 100 red
解析:
这是因为,this.color=“red”,
mian(){
Car c = new Car();
Car c1 = new Car(100);:调用的是有参构造器,其中静态color属性,静态变量只会随着类的加载执行一次(在调用无参构造器时),则不再执行就意味着,此时的color保存的是被修改后的值
什么是静态变量?该类的所有对象共享的变量,任何一个该类的对象去访问它的时候,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
在这里提出一个问题:静态变量通过构造器赋了一个新值后,后续访问这个静态变量时的得到的是赋值前的值还是赋值后的值?
解析:
得到的是赋值后的值。
这是因为,静态变量是与类关联而不是与实例对象关联的。当通过构造器或其他方法更改静态变量的值时,这个变化会影响到所有使用这个静态变量的地方,包括之前创建的实例对象以及后续穿点的实例对象。
p438_作业2
题目:1.计算器接口具有work方法,功能是运算,有一个手机类Cellphone,定义方法testWork测试计算机功能,调用计算接口的work方法//接口参数多态
2.要求调用CellPhone对象的testWork方法,使用上匿名内部类
(Homework04.java)
package com.zyj.U10Homework;
public class Homework04 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
cellphone.testWork(new ICalculate() {
@Override
public double work(double n1, double n2) {
return n1 + n2;
}
},10,8);
/*
cellphone.testWork(new ICalculate() {
@Override
public double work(double n1, double n2) {
return 0;
}
},10,8);
*解读以上代码:
他的编译类型:ICalculate 运行类型:匿名内部类
*/
}
}
/*1.计算器接口具有work方法,功能是运算,有一个手机类Cellphone,定义方法testWork测试计算机功能,调用计算接口的work方法
2.要求调用CellPhone对象的testWork方法,使用上匿名内部类*/
//编写接口
interface ICalculate{
//work方法完成这样的计算,交给匿名内部类完成
public double work(double n1,double n2);
}
class Cellphone{
//在实现这个方法时,传入了接口,也就是实现了接口
//当调用下方法时,直接传入一个实现了ICalculate接口的匿名内部类即可
//该匿名内部类,可以灵活地实现work,完成不同的计算任务
//匿名内部类,可以虚拟实例化接口类、抽象类对象并作为参数传递
public void testWork(ICalculate iCalculate,double n1,double n2){
//动态绑定 根据传入的对象时及类型动态绑定到相应的方法
double work = iCalculate.work(n1, n2);
System.out.println(work);
}
}
接口参数多态:本质是通过接口实现了多态性,将实现了接口的对象作为参数传递给方法,实现了灵活地调用和扩展。
为什么在类的方法中传入接口的参数就相当于实现了接口?
当一个类的方法中传入接口参数时,实际上相当于该类实现了该接口的方法。虽然在累的声明中并没有使用关键字implements来显示地表示该类实现了接口,但通过方法的参数类型为接口,可以达到相同的效果。接口定义了一组方法的规范要求实现了该接口的类必须提供这些方法的具体实现。通过传入接口类型的参数,该方法就能够接收实现了该接口的对象,并调用对象所具备的方法。
动态绑定:根据传入的对象以及类型动态绑定到相应的方法,从而实现不同对象的多态调用和行为
在本题中如何体现使用了匿名内部类?
在主方法中调用testWork方法时,直接传入一个实现了该接口的匿名内部类,至于想完成怎样的计算,可以在匿名内部类里面实现work方法。这是由于匿名内部类可以非常灵活的实现不同计算。
p439_作业3
HomeWork06.java
1.有一个交通工具接口类Vehicles,有work接口
2.有Horse类和Boat类分别实现实现Vehicles
3.创建交通工具工厂类,有两个方法分别获得交通工具Horse和Boat
4.有Person类,有name和Vehicles属性,在构造器中为两个属性赋值
5.实例化Person对象“唐僧”,要求一般情况下用Horse作为交通工具,遇到大河时用Boat作为交通工具
p446_异常体系图
异常体系图的小结:
a.异常分为两大类,运行时异常和编译时异常
b.运行时异常,编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常
c.对于运行时异常,可以不做处理,因为这类异常很普遍,若全处理可能对程序的可读性和运行效率产生影响
d.编译时异常,是编译器要求必须处置的异常
1.执行过程中所发生的异常事件可分为两大类:
Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误,资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OutOfMemoryError(内存溢出) 是严重错误,程序会崩溃
Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。Exception 分为两大类:运行时异常(程序运行时,发生的异常)和编译时异常(编程时,编译器检查出的异常)
2.常见的运行时异常 (RuntimeException)
NullPointerException 空指针异常:
当应用程序试图在需要对象的地方使用 null 时,抛出该异常
ArithmeticException 数学运算异常:
当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例
ArrayIndexOutOfBoundsException 数组下标越界异常:
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引
ClassCastException 类型转换异常:
当试图将对象强制转换为不是实例的子类时,抛出该异常
NumberFormatException 数字格式不正确异常:
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常(使用异常我们可以确保输入是满足条件数字)
p448_异常课堂练习
3.常见的编译异常:
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
SQLException (操作数据库时,查询表可能发生异常)
IOException (操作文件时,发生的异常)
FileNotFoundException( 当操作一个不存在的文件时,发生异常)
ClassNotFoundException (加载类,而该类不存在是,发生异常)
EOFException (操作文件,到文件末尾,发生异常)
IllegalArguementException( 参数异常)
p449_异常处理机制
4.异常处理的方式:
try-catch-finally :程序员在代码中捕获发生的异常,自行处理
throws :将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
try-catch 方式处理异常(快捷键 ctrl + alt + t)
try {
// 可疑代码;
// 将异常生成对应的异常对象,传递给 catch 块;
} catch (Exception e) {
//捕获到异常
//1.当异常发生时
//2.系统将异常封装成Exception 对象 e ,传递给catch
//3.得到异常对象后,程序员自己处理
//4.如果没有发生异常,catch代码块不执行
} finally {
//不管try{代码}代码块是否有异常发生,始终要执行finally。
//释放资源等;
}
JVM的处理机制:(处理异常)
1.输出异常信息
2.退出程序
如果没有try-catch或throws,则程序默认使用throws
p450_try-catch
try-catch 方式处理异常(快捷键 ctrl + alt + t)
1.基本语法
`try {
// 可疑代码;
// 将异常生成对应的异常对象,传递给 catch 块;
} catch (异常) {
// 对异常的处理;
} finally {
//释放资源等;
//如果没有finally也是被允许的
}`
2.如果异常发生了,则异常发生后面的代码不会执行,直接进入到 catch 块
3.如果异常没有发生,则顺序执行 try 的代码块,不会进入到 catch
4.如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用如下代码- finally
5.如果 try 代码块有可能有多个异常,可以使用多个 catch 分别捕获不同的异常,相应处理。要求子类异常写在前面,父类异常写在后面
6.可以进行 try-finally 配合使用, 这种用法相当于没有捕获异常, 因此程序会直接崩掉/退出。应用场景,就是执行一段代码,不管是否发生异常, 都必须执行某个业务逻辑
p451_try-Catch异常处理
try-catch-finally执行顺序小结:
1.如果没有出现异常,则执行try块中所有语句,不执行catch块中语句,如果有finally,最后还需要执行finally语句。
2.如果出现异常,则try块中异常发生后,try剩下的语句不再执行。将执行catch块中语句,如果有finally,最后还需要执行finally里面的语句。
p452_try-catch最佳实践
如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止
Scanner sc = new Scanner(System.in);
int num = 0;
String inputStr = "";
while(true){
sout("请输入一个整数");
inputStr = scanner.next();
try{
num = Integer.parseInt(inputStr);
break;
}catch(NumberFormatException e){
sout("你输入的不是一个整数");
}
}
sout("你输入的值为 = " + num);
p453_throws入门
1.如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明异常,表名该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
2.这方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
p454_throws使用细节
1.运行异常默认抛出,编译异常必须处理。
2.对于编译异常,程序中必须处理,比如try-catch或者throws
3.子类重写父类的方法时,对抛出异常的规定:
子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型
4.在throws过程中,如果有方法try-catch,就相当于处理异常,就可以不必throws
5.对于运行异常,程序中如果没有处理,默认就是throws的方式处理
p455_自定义异常
自定义异常的步骤:
1.定义类:自定义异常类名(程序员自己写的)继承Exception或RuntimeException
2.如果继承Exception,属于编译异常
3.ruguojichengRuntimeException,属于运行异常(一般来说,继承RuntimeException)
package com.zyj.customexception;
public class CustomException {
public static void main(String[] args) {
int age = 180;
//要求范围在18~120之间,否则抛出一个自定义异常
if (!(age>=18&&age<=120)){
throw new ageException("年龄需要在18~120之间");
}
System.out.println("你的年龄范围正确");
}
}
//自定义一个异常
class ageException extends RuntimeException{
public ageException(String message){
super(message);
}
}
p456_throw和throws区别
throw和throws区别
p458_异常课后作业2
程序执行顺序:
首先在main方法中执行try的func()方法
func方法中抛出一个运行异常,则sout(“A”)不再执行
但是finally{sout(“B”)}是必须执行的
执行catch中的sout(“C”)
由于捕获了异常,即这个异常被处理过,程序不会退出
执行sout(“D”)
p460_八大wrapper类
包装类的分类 Wrapper
- 针对八种基本数据类型相应的引用类型–包装类
- 有了类的特点,就可以调用类中的方法
p461_装箱和拆箱
包装类和基本数据的转换
1)jdk5前的手动装箱和拆箱方式,拆箱:基本数据类型->包装类型,反之,拆箱
//手动装箱:int->Integer
int n1 = 100;
Integer integer = new Integer(n1);
Integer integer = Integer.valueOf(n1);
//手动拆箱:Integer->int
int i = integer.intValue();
2)jdk5以后(含jdk5)的自动装箱和拆箱方式
3)自动装箱底层调用的是valueOf方法,比如Integer.valueOf()
p463_包装方法
p464_Integer面试题
p466_String结构剖析
1.String对象用于保存字符串,也就是一组字符序列
2.“jack”字符串常量,双引号括起的字符序列
3.字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
4.String类有很多构造器,构造器的重载
常用的构造器:
5.String类实现了接口Serializable【String串行化:可以在网络传输】
6.String类实现了接口Comparable【String可以比较大小】
7.String是一个final类,不能被其他类继承
8.String有属性private final char value[];用于存放字符串内容
9.一定要注意:value是一个final类型,(不可以修改:value不能指向新的地址,但是单个字符内容是可以变化)
·Eg
p467_String创建剖析
两种创建String对象的区别
方法1、直接赋值String s = “hsp”
方法2、调用构造器 String s2 = new String(“hsp”);
方法一:先从常量池查看是否有“hsp”数据空间,如果有,直接指向,如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址
方法二:先在堆中创建空间,里面维护了value属性,指向常量池的hsp空间。如果常量池没有“hsp”,重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址
这两种方式的内存分布图:
p468_String测试题1
b==b.intern();F 注:指向的对象不同,解释如下图:b指向堆;b.inern()指向的是常量池。
注:当调用intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回String对象的引用
b.intern方法最终返回的是常量池的地址(对象)
p469_String测试题2
p470_String对象特性1
1、String是一个final类,代表不可变得字符序列
2、字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的
先创建一个stringBuilder sb = StringBuilder()
执行 sb.append(“hello”)
sb.append(“abc”)
String c = sb.toString()
最后其实是c指向堆中的对象(String)value[]->池中“helloabc”
总结:底层是Stringbuilder sb = new StringBuilder();sb.append(a);sb.append(b);sb是在堆中,并且append是在原来字符串的基础上追加的。
重要规则:String c1 = “ab” + “cd” ;常量追加,看的是池。String c1 = a + b;变量相加,是在堆中。
P471_String特性2
String s6 = (s1 + s2).intern();//指向s1+s2结果内容的对应常量,即s1
答案:T T
p472_String常用方法1
p473_String常用方法2
package org.example.String_;
public class StringMethod02 {
public static void main(String[] args) {
//1.toUpperCase转换大写
String s = "hello";
System.out.println(s.toUpperCase());//HELLO
//2.toLowerCase
System.out.println(s.toLowerCase());//hello
//3.concet拼接字符串
String s1 = "宝玉";
s1 = s1.concat("林黛玉").concat("薛宝钗");
System.out.println(s1);
//4.replace 替换字符串中的字符
s1 = "宝玉 and 林黛玉 薛宝钗 薛宝钗";
//在s1中,将所有的林黛玉 替换成薛宝钗
//老韩解读:s1.replace方法执行后,返回的结果才是替换过的
s1 = s1.replace("林黛玉","薛宝钗");
s1 = s1.replace("宝玉","jack");
System.out.println(s1);
//5.split分割字符串,对于某些分割字符,需要转义如 | \\
String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
//老韩解读:1.以,为标准对peom进行分割
// 2.在对字符串进行分割时,如果有特殊字符,需要加入 转义符、
String[] split = poem.split(",");
poem = "E:\\aaa\\bbb";
split = poem.split("\\\\");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
}
//6.toCharArray 转换成字符数组
s = "happy";
char[] chs = s.toCharArray();
for (int i = 0; i < chs.length; i++) {
System.out.println(chs[i]);
}
//7.compareTo 比较两个字字符串的大小,如前者大
//则返回正数,后者大,返回负数,如果相等,返回0
String a = "jchn";
String b = "jack";
System.out.println(a.compareTo(b));
}
}
p474_String常用方法3
//8.format格式字符串
/*占位符:%s-字符串 %c-字符 %d-整形 %.2d-浮点型
%.2f-实用小数点来替换,替换后,只会保留小数点两位,并且进行四舍五入的处理*/
String name = "john";
int age = 10;
double score = 98.3 / 3;
char gender = '男';
String info = "我的名字是" + name + "年龄是" + age;
String info2 = String.format("我的姓名是%s 年龄是%d 成绩是%.2f",name,age,score);
System.out.println(info2);
p475_StringBuffer类
1.1StringBuffer代表可变的字符序列,可以对字符串内容进行增删
2.很多方法与String相同,但StringBuffer是可变长度的
3.StringBuffer是一个容器
老韩解读:
- StringBuffer的直接父类 是 AbstractStringBuilder
- StringBuffer实现了 Serializable ,即StringBuffer的对象可以串行化
- 再父类中 AbstractStringBuilder 有属性
IO流
1、文件
-
文件流:文件在程序中以流的形式来操作
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
-
创建文件对象相关构造器方法
- new File(String pathname)
- new File(File parent,String child)
- new File(String parent,String child)