JavaStudy2(枚举类+oop高级部分练习+异常)—B站韩顺平
1.枚举类
1.1枚举类入门
要求创建季节(Season) 对象,请设计并完成。Enumeration01.java
class Season{//类
private String name;
private String desc;//描述
//构造器
//getXX
//setXX
}
代码:
public class Enumeration01 {
public static void main(String[] args) {
Season spring = new Season("春天","温暖");
Season winter = new Season("冬天","温暖");
Season summer = new Season("夏天","温暖");
Season autumn = new Season("秋天","温暖");
//因为对于季节而已,他的对象(具体值),是固定的四个,不会有更多
//安老师的这个设计类的思路,不能体现季节是固定的四个对象
// 因此,这样的设计不好===> 枚举类[枚: 一个一个 举: 例举 , 即把具体的对象一个一个例举出来的类
// 就称为枚举类]
}
}
class Season{
private String name;//季节名
private String desc;//描述
public Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
1.2分析问题
1.2.1创建Season对象有以下特点
- 季节的值是有限的几个值(spring, summer, autumn, winter)
- 只读,不需要修改。
1.2.2解决方案-枚举
-
枚举对应英文(enumeration, 简写 enum)
-
枚举是一组常量的集合。
-
可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象。
1.2.3枚举的二种实现方式
1.自定义类实现枚举
1.1自定义类实现枚举-应用案例
- 不需要提供setXxx方法,因为枚举对象值通常为只读
- 对枚举对象?属性使用fianl+static共同修饰,实现底层优化
- 枚举类对象名通常使用全部大写,常量命名规范
- 枚举对象根据需要,也可以有多个属性
1.2代码演示:
public class Enumeration02 {
public static void main(String[] args) {
}
}
//演示自定义枚举类
class Season1{
private String name;//季节名
private String desc;//描述
//定义了四个对象
public static Season1 SPRING = new Season1("春天","温暖");
public static Season1 WINTER = new Season1("冬天","温暖");
public static Season1 SUMMER = new Season1("夏天","温暖");
public static Season1 AUTUMN = new Season1("秋天","温暖");
//1.将构造器私有化,目的防止直接new
//2.去掉setxxx方法,防止属性被更改
//3.在Season内部 ,直接创建固定的对象
//4.优化,可以加入 final 修饰符
private Season1(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
1.3小结:进行自定义类实现枚举,有如下特点:
- 构造器私有化
- 本类内部创建一组对象【四个,春夏秋冬】
- 对外暴露对象(通过为对象添加public final static 修饰符)
- 可以提供get方法,但是不要提供set
2.使用 enum 关键字实现枚举
2.1enum 关键字实现枚举-快速入门
//如果使用了 enum 来实现枚举类
//1. 使用关键字 enum 替代 class
//2.将public static Season3 SPRING = new Season3("春天","温暖"); 直接替换为
// SPRING("春天","温暖"); 解读 常量名(实参列表)
//3.如果有多个常量(对象),使用","逗号隔开即可
//4.如果使用enum 来实现枚举类,要求将定义的常量对象,写在枚举类内最前面
代码演示:
public class Enumeration03 {
public static void main(String[] args) {
System.out.println(Season3.SPRING);
System.out.println(Season3.SUMMER);
}
}
//演示使用enum 关键字来实现枚举类
enum Season3{ //类
// //定义了四个对象
// public static Season3 SPRING = new Season3("春天","温暖");
// public static Season3 WINTER = new Season3("冬天","温暖");
// public static Season3 SUMMER = new Season3("夏天","温暖");
// public static Season3 AUTUMN = new Season3("秋天","温暖");
//如果使用了 enum 来实现枚举类
//1. 使用关键字 enum 替代 class
//2.将public static Season3 SPRING = new Season3("春天","温暖"); 直接替换为
// SPRING("春天","温暖"); 解读 常量名(实参列表)
//3.如果有多个常量(对象),使用","逗号隔开即可
//4.如果使用enum 来实现枚举类,要求将定义的常量对象,写在枚举类内最前面
SPRING("春天","温暖"),WINTER("冬天","温暖"),SUMMER("冬天","温暖"),AUTUMN("秋天","温暖");
private String name;//季节名
private String desc;//描述
private Season3(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Season3{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
2.2enum 关键字实现枚举注意事项
-
当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类, 而且是一个 final 类[如何证明],老师使用 javap 工 具来演示
-
传统的 public static final Season2 SPRING = new Season2(“春天”, “温暖”); 简化成 SPRING(“春天”, “温暖”), 这里必 须知道,它调用的是哪个构造器.
-
如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
-
当有多个枚举对象时,使用,间隔,最后有一个分号结尾
-
枚举对象必须放在枚举类的行首
2.3enum 关键字实现枚举-课堂练习
- 下列输出是什么
public class EnumExercise01 {
public static void main(String[] args) {
Gender boy = Gender.BOY; //ok
Gender boy2 = Gender.BOY; //ok
System.out.println(boy);//输出BOY //本质就是调用Gender的父类Enum的toSring方法
System.out.println(boy == boy2);//true
}
}
enum Gender{
BOY,GIRL;
}
2.4enum 常用方法说明
enum 常用方法应用实例
-
toString():返回的是当前对象名,子类可以重写该方法,用于返回对象的属性信息
-
name():返回当前对象名(常量名),子类中不能重写
-
ordinal():返回会当前对象的位置号,默认从0开始
-
values:返回当前枚举类中所有的常量
-
valuesOf():将字符串转换成枚举对象,要求字符串必须为已有常量名,否则报异常!
-
compareTo():比较两个枚举常量,比较的就是编号!
public class EnumMethod {
public static void main(String[] args) {
//使用Season4枚举类,来演示各种方法
Season4 spring = Season4.SPRING;
//输出枚举对象的名字
System.out.println(spring.name());//SPRING
//ordinal()输出的是该枚举对象的次序/编号,从0开始编号
// SPRING 枚举对象是第一个,因此输出 0
System.out.println(spring.ordinal());//0
//从反编译可以看到 values 方法,返回 Season4[]
//含有定义的所有枚举类对象
Season4[] values = Season4.values();
System.out.println("====遍历取出枚举对象(增强for)====");
for (Season4 season4 : values) {//增强for循环
System.out.println(season4);
}
//valueOf:将字符串转换成枚举对象,要求字符串必须 为已有的常量名,否则报异常
//执行流程
//1.根据你输入的"AUTUMN"到Season2的枚举对象去查找
//2.如果找到了,就返回,如果没有找到,就报错
Season4 spring1 = Season4.valueOf("SPRING");
System.out.println("autumn1 = " + spring1);
System.out.println(spring1 == spring);
//compareTo:比较两个枚举常量,比较的就是编号
//1.就是把Season4.SPRING 枚举对象的编号 和 Season4.AUTUMN枚举对象的编号作比较
/*
public final int compareTo(E o) {
return self.ordinal - other.ordinal;本身对象和其他对象编号相减
}
Season4.SPRING 的编号[0] - Season4.AUTUMN 的编号[3]
*/
System.out.println(Season4.SPRING.compareTo(Season4.AUTUMN));//-3
}
}
enum Season4{ //类
SPRING("春天","温暖"),WINTER("冬天","温暖"),SUMMER("冬天","温暖"),
AUTUMN("秋天","温暖");
private String name;//季节名
private String desc;//描述
Season4() {//无参构造
}
private Season4(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Season3{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
enum 细节
- 使用enum关键字后,就不能在继承其他类了,因为enum会隐式继承Enum,而Java是单继承机制
- enum实现的枚举类,任然是一个类,所以还是可以实现接口的。
代码演示:
public class EnumDetail {
public static void main(String[] args) {
Music.CLASSICMUSIC.playing();//播放音乐
}
}
//1. 使用enum关键字后,就不能在继承其他类了,因为enum会隐式继承Enum,而Java是单继承机制
//2. enum实现的枚举类,任然是一个类,所以还是可以实现接口的。
interface IPlaying{
public void playing();
}
enum Music implements IPlaying{
CLASSICMUSIC;
@Override
public void playing() {
System.out.println("播放音乐");
}
}
2.注解
2.1注解的理解
-
注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
-
和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
-
在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替 java EE 旧版中所遗留的繁冗代码和 XML 配置等
2.2基本的注解Annotation介绍
使用Annotation是要在其前面加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素。
- @Override:限定的某个方法,是重写父类的方法,该注解只能用于方法
- @Deprecated:用于表示某个程序元素(类,方法等)已过时
- @SuppressWarnings:抑制编译器警告
2.3基本的 Annotation 应用案例
- @Override 注解的案例
代码演示:
public class Override_ {
public static void main(String[] args) {
}
}class Father{//父类
public void fly(){
System.out.println("Father fly...");
}
public void say(){
}
}
class Son extends Father {//子类
//解读
// 1. @Override 注解放在 fly 方法上,表示子类的 fly 方法时重写了父类的 fly
// 2. 这里如果没有写 @Override 还是重写了父类 fly
// 3. 如果你写了@Override 注解,编译器就会去检查该方法是否真的重写了父类的
// 方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误
// 4. 看看 @Override 的定义
// 解读: 如果发现 @interface 表示一个 注解类
/*
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override { }
*/
@Override //说明
public void fly() {
System.out.println("Son fly....");
}
@Override public void say() {
}
}
-
@Deprecated 注解的案例
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时代码
代码演示:
public class Deprecated_ {
public static void main(String[] args) {
A a = new A();
a.hi();
System.out.println(a.n1);
}
}
//老韩解读
// 1. @Deprecated 修饰某个元素, 表示该元素已经过时
// 2. 即不在推荐使用,但是仍然可以使用
// 3. 查看 @Deprecated 注解类的源码
//4. 可以修饰方法,类,字段, 包, 参数 等等
// 5. @Deprecated 可以做版本升级过渡使用
/*
@Documented @Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated { }
*/
@Deprecated
class A {
@Deprecated
public int n1 = 10;
@Deprecated
public void hi(){
}
}
- @SuppressWarnings 注解的案例
@SuppressWarnings: 抑制编译器警告
代码演示:
public class SuppressWarnings_ {
//当我们不希望看到这些警告的时候,可以使用 SuppressWarnings 注解来抑制警告信息
// 2. 在{""} 中,可以写入你希望抑制(不显示)警告信息
//3. 可以指定的警告类型有很多
//4. 关于 SuppressWarnings 作用范围是和你放置的位置相关
// 比如 @SuppressWarnings 放置在 main 方法,那么抑制警告的范围就是 main
// 通常我们可以放置具体的语句, 方法, 类.
// 5. 看看 @SuppressWarnings 源码
// (1) 放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE
// (2) 该注解类有数组 String[] values() 设置一个数组比如 {"rawtypes", "unchecked", "unused"} /*
@SuppressWarnings({"all"})
//@SuppressWarnings({"rawtypes", "unchecked", "unused"})
public static void main(String[] args) {
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("mary");
}
}
.3.1可以指定的警告类型有:
可以指定的警告类型有:
// 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,抑制与未用的程式码及停用的程式码相关的警告
2.4JDK 的元 Annotation(元注解, 了解)
JDK 的元 Annotation 用于修饰其他 Annotation
元注解: 本身作用不大,看源码时,可以知道他是干什么.
2.4.1元注解的种类
-
Retention //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
-
Target // 指定注解可以在哪些地方使用
-
Documented //指定该注解是否会在 javadoc 体现
-
Inherited //子类会继承父类注解
@Retention 注解
只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留多长时间, @Rentention 包含一个 RetentionPolicy
类型的成员变量, 使用 @Rentention 时必须为该 value 成员变量指定值:
@Retention 的三种值
-
RetentionPolicy.SOURCE: 编译器使用后,直接丢弃这种策略的注释
-
RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解。 这是默认 值
-
RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以 通过反射获取该注解
@Target
@Documented
@Inherited 注解
3.面向对象(高级)练习题回顾
- 写出结果
public class HomeWork01 {
public static void main(String[] args) {
Car car = new Car();
Car car1 = new Car(100);
System.out.println(car);
System.out.println(car1);
}
}
class Car{
double price = 10;
static String color = "white";
public String toString(){
return price + color;
}
public Car (){
this.price = 9;
this.color = "red";
}
public Car(double price) {
this.price = price;
}
}
public class HomeWork02 {
public static void main(String[] args) {
Frock frock1 = new Frock(); //100100
Frock frock2 = new Frock(); //100200
Frock frock3 = new Frock(); //100300
}
}
class Frock{
private static int currentNum = 100000;
private int seriaNumber;
public static int getCurrentNum(){
currentNum +=100;
return currentNum;
}
public static void getCurrentNum(int currentNum) {
Frock.currentNum = currentNum;
}
public void setSeriaNumber(int seriaNumber) {
this.seriaNumber = seriaNumber;
}
public Frock() {
this.seriaNumber = getCurrentNum();
System.out.println(this.seriaNumber);
}
}
public class HomeWork03 {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
dog.shout();
cat.shout();
}
}
abstract class Animal{
abstract void shout();
}
class Dog extends Animal{
@Override
void shout() {
System.out.println("旺旺");
}
}
class Cat extends Animal{
@Override
void shout() {
System.out.println("喵喵");
}
}
- 匿名内部类题
public class HomeWork04 {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
/*
new Calculator() {
@Override
public double work(double n1, double n2) {
return (n1 + n2);
}
},10,8 同时也是一个对象
他的编译类型 Calculator, 他的运行类型是 匿名内部类;
*/
cellPhone.testWork(new Calculator() {
@Override
public double work(double n1, double n2) {
return (n1 + n2);
}
},10,8);
}
}
interface Calculator{
double work( double n1 , double n2);
}
class CellPhone{
//1.解读,当我们调用testWork方法时,直接传入一个实现了 Calculator接口的匿名内部类即可;
//2.该匿名内部类,可以灵活的实现work,完成不同的计算任务
public void testWork(Calculator calculator ,double n1 ,double n2){
double result = calculator.work(n1 ,n2);//动态绑定
System.out.println("最后的结果是" + result);
}
}
内部类基础薄弱,自主补充练习
//成员内部类
public class InnerClassTest01 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.fun();
}
}
class Outer{
private String name = "外部属性";
//定义一个内部类(成员内部类)
class Inner{
private String name = "内部类属性";
public void print(){
System.out.println(Outer.this.name);
//这里遵守就近原则,当内部类和外部内属性变量名相同时,需要用外部类名.this.变量名调用
}
}
public void fun(){
Inner inner = new Inner();
inner.print();
}
//在外部类内部(静态方法内)创建成员内部类对象
public static void f(){
Outer13.Inner inner = new Outer13().new Inner();
}
}
//成员内部类实现多继承
public class InnerClassTest02 {
public static void main(String[] args) {
Outer12 outer1 = new Outer12();
System.out.println(outer1.name2());
System.out.println(outer1.age());
}
}
class A1{
private String a1 = "A1的私有属性";
public String getA1() {
return a1;
}
}
class B1{
private int age1 = 20;
public int getAge() {
return age1;
}
}
class Outer12 {
@SuppressWarnings("all")//抑制警告
private class InnerClassA extends A1{
@Override
public String getA1() {
return super.getA1();
}
}
private class InnerClassB extends B1{
@Override
public int getAge() {
return super.getAge();
}
}
public String name2(){
InnerClassA innerClassA = new InnerClassA();
return innerClassA.getA1();
}
public int age(){
return new InnerClassB().getAge();
}
}
//局部内部类
public class InnerClassTest03 {
public static void main(String[] args) {
Outer14 outer14 = new Outer14();
outer14.display();
}
}
class Outer14{
private int age = 5;
public void display(){
//方法(局部)内部类嵌套在方法里面
class Inner{
public void fun(){
System.out.println(age);
}
}
Inner inner = new Inner();
inner.fun();
}
}
//匿名内部类
public class InnerClassTest04 {
public static void main(String[] args) {
Outer15 outer15 = new Outer15();
outer15.display(3);
}
}
interface MyInterface {//接口
//接口方法中没有方法体
void test();
}
class Outer15 {
private int num = 5;
public void display(int temp){
new MyInterface(){
@Override
public void test() {
System.out.println("匿名内部类实现了MyInterface接口");
System.out.println(temp);
}
}.test();
}
}
public class HomeWork05 {
public static void main(String[] args) {
A a = new A();
a.f1();
}
}
class A{
private String name = "小刚";
public void f1(){
class B{
private final String NAME = "小名";
public void show(){
System.out.println(NAME);
System.out.println(A.this.name);//内部类和外部类属性重名的情况下
}
}
B b = new B();
b.show();
}
}
6.
public class HomeWork06 {
public static void main(String[] args) {
Person person = new Person("唐僧",new Horse());
person.common();//骑马
person.passRiver();//坐船
person.passHireHill();// 飞机
}
}
/*1.有一个交通工具接口类Vehicles,有work接口
2.有Horse类和Boat类分别实现Vehicles
3.创建交通工具工厂类,有两个方法分别获得交通工具Horse和Boat
4.有Person类,有name和Vehicles属性,在构造器中为两个属性赋值
5.实例化Person对像“唐僧”,要求一般情况下用Horse作为交通工具,遇到大河时用Boa作为交通工具
6.增加一个情况,如果唐僧过火焰山,使用 飞机 ==>程序扩展性,我们前面的程序非常号扩展
*/
public interface Vehicles {//接口 工具
//抽象方法
public abstract void work();
}
public class Factory {
//马始终是同一匹马
private static Horse horse = new Horse();//饿汉式
//把构造器私有化,不让创建对象
private Factory(){}
//这里我们将方法 做成static
public static Horse getHorse(){
// return new Horse();
return horse;
}
public static Boat getBoat(){
return new Boat();
}
public static Plane getPlane(){
return new Plane();
}
}
public class Horse implements Vehicles{
@Override
public void work() {
System.out.println("骑马");
}
}
public class Plane implements Vehicles{
@Override
public void work() {
System.out.println("飞机");
}
}
class Boat implements Vehicles {
@Override
public void work() {
System.out.println("坐船");
}
}
public class Person {
//4.有Person类,有name和Vehicles属性,在构造器中为两个属性赋值
private String name;
private Vehicles vehicles;
//在创建人对象时,会给他分配一个交通工具
public Person(String name, Vehicles vehicles) {
this.name = name;
this.vehicles = vehicles;
}
//5.实例化Person对像“唐僧”,要求一般情况下用Horse作为交通工具,遇到大河时用Boat作为交通工具
//这里涉及到一个编程思想,就是可以把具体的要求,封装成方法->这就是编程思想
public void passRiver(){//过河
//先得到船
//判断一下,当前的 vehicles 属性是null,就获取一艘船
// Boat boat = Factory.getBoat();
// boat.work();
//如何防止始终使用的是传入的马,instanceOf
//if (vehicles == null){ 更改前只是判断是否为NULL
//vehicles instanceof Boat 是判断当前的 vehicles是不是Boat或者空
// (1)vehicles ==null (vehicles instanceof Boat)->false 取反
//(2)vehicles == 马对象 (vehicles instanceof Boat)->false 取反
//(3)vehicles == 船对象 (vehicles instanceof Boat)->true 取反
if (!(vehicles instanceof Boat)){
//这里使用了多态
vehicles = Factory.getBoat();
}
//这里体现了接口的调用
vehicles.work();
}
public void common(){//通常情况
//得到马🐎
//判断一下,当前的 vehicles 属性不是马,就获取一匹马
if(!(vehicles instanceof Boat)){
//这里使用了多态
vehicles = Factory.getHorse();
}
//这里体现了接口的调用
vehicles.work();
}
public void passHireHill(){//通常情况
//飞机
//判断一下,当前的 vehicles 属性不是飞机,就获取一匹飞机
if(!(vehicles instanceof Plane)){
//这里使用了多态
vehicles = Factory.getPlane();
}
//这里体现了接口的调用
vehicles.work();
}
}
public class HomeWork07 {
/*
内部类练习
有一个Car类,有属性temperature(温度),车内有Air(空调)类,有吹风的功能flow,
A会监视车内的温度,如果温度超过40度则吹冷气。如果温度低于0度则吹暖气,如果在这之
间则关掉空调。实例化具有不同温度的Ca对象,调用空调的flow方法,测试空调吹的风是否
正确。//体现类与类的包含关系的案例类(内部类【成员内部类】)
*/
public static void main(String[] args) {
//两种方法,第一种比较好
Car car = new Car(50);
car.getAir().flow(); //大于40吹冷气
Car car1 = new Car();
car1.carFLow(-5); //小于0吹暖气
car1.carFLow(26);//正常温度,关掉空调
}
}
public class Car {
private double temperature;
public Car() {
}
public Car(double temperature) {
this.temperature = temperature;
}
class Air{
public void flow(){
if (temperature > 40.0){
System.out.println("大于40吹冷气");
}else if(temperature < 0){
System.out.println("小于0吹暖气");
}else {
System.out.println("正常温度,关掉空调");
}
}
}
public Air getAir(){
return new Air();
}
public void carFLow( double temperature){
this.temperature = temperature;
Air air = new Air();
air.flow();
}
}
public class HemoWork08 {
public static void main(String[] args) {
Color green = Color.GREEN;
green.show();
//比较一下
//switch()中,放入枚举值
//在每个case后,直接写上在枚举类中,定义的枚举类对象即可
switch (green){
case RED:
System.out.println("匹配到红色");
break;
case BLUE:
System.out.println("匹配到蓝色");
break;
case BLACK:
System.out.println("匹配到黑色");
break;
case GREEN:
System.out.println("匹配到绿色");
break;
case YELLOW:
System.out.println("匹配到黄色");
break;
default:
System.out.println("没匹配到");
}
}
}
public enum Color implements IColor{
RED(255,0,0),BLUE(0,0,255),
BLACK(0,0,0), YELLOW(255,255,0),
GREEN(0,255,0);//枚举值(对象)
private int redValue;
private int blueValue;
private int greenValue;
Color(int redValue, int blueValue, int greenValue) {
this.redValue = redValue;
this.blueValue = blueValue;
this.greenValue = greenValue;
}
@Override
public void show() {
System.out.println(redValue + "," + blueValue + "," + greenValue);
}
}
interface IColor{
public void show();
}
4.异常(Ctrl+Alt+T)
4.1异常介绍
4.1.1异常捕获
public class Exception01 {
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;
//int result = num1/num2; //程序就会出现(抛出)异常 ArithmeticException
//1.如果程序员,认为一段代码可能出现异常/问题,可以使用 try-catch 异常处理机制来解决
//2.从而保证程序的健壮性 //将该代码块->选中->快捷键 ctrl + alt + t -> 选中 try-catch
//3. 如果进行异常处理,那么即使出现了异常,程序可以继续执行
try {
int result = num1/num2;
} catch (Exception e) {
//e.printStackTrace();
System.out.println("出现异常的原因是:" + e.getMessage());//输出异常信息
}
System.out.println("程序继续执行");
}
}
4.1.2异常介绍
1. 基本概念
Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法
错误和逻辑错误不是异常)
执行过程中所发生的异常事件可分为两大类
-
Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源
耗尽等严重情况。比如:StackOverflowError【栈溢出】和OOM(out of
memory),Error是严重错误,程序会崩溃。 -
Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针
对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中
断等等,Exception分为两大类:运行时异常[程序运行时,发生的异常]和编
译时异常[编程时,编译器检查出的异常]。
2. 异常体系图
3. 异常体系图的小结
- 异常分为两大类,运行时异常和编译时异常
- 运行时异常,编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常
- 对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响
- 编译时异常,是编译器要求必须处置的异常。
4.1.3常见的运行时异常
-
NullPointerException:空指针异常
-
ArithmeticException:数学运算异常
-
ArrayIndexOutOfBoundsException:数组下标越界异常
-
ClassCastException:类型转换异常
-
NumberFormatException:数字格式不正确异常[]
-
NullPointerException 空指针异常 NullPointerException_.java 当应用程序试图在需要对象的地方使用 null 时,抛出该异常,看案例演示
public class NullPointerException_ {
public static void main(String[] args) {
String name = null;
System.out.println(name.length());
}
}
2. ArithmeticException数学运算异常 ArithmeticException_.java
当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例,
3. ArrayIndexOutOfBoundsException 数组下标越界异常
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引
public class ArrayIndexOutOfBoundsException_ {
public static void main(String[] args) {
int[] nums ={1,2,3};
for (int i = 0; i < 4; i++) {
System.out.println(nums[i]);
}
}
}
4.ClassCastException 类型转换异常
当试图将对象强制转换为不是实例的子类时,抛出该异常。例如,以下代码将生成一个 ClassCastException
public class ClassCastException_ {
public static void main(String[] args) {
A b = new B(); //向上转型
B b2 = (B)b; //向下转型,这里是ok
C c = (C)b; //这里抛出ClassCastException
}
}
class A{}
class B extends A{}
class C extends A{}
5**.NumberFormatException** 数字格式不正确异常
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常 => 使用异常我们
可以确保输入是满足条件数字.
public class NumberFormatException_ {
public static void main(String[] args) {
//String name = "123";
//int num = Integer.parseInt(name);//123
String name = "覃波";
int num = Integer.parseInt(name);//异常 NumberFormatException
}
}
4.1.4编译时异常
代码演示:
public class Exception02 {
public static void main(String[] args) {
try {
FileInputStream fis;
fis = new FileInputStream("d:\\aa.jpg");
int len;
while ((len = fis.read()) != -1) {
System.out.println(len);
}
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
异常练习
4.2异常处理
4.2.1基本介绍
异常处理就是当异常发生时,对异常处理的方式。
4.2.2异常处理的方式
-
try-catch-finally
程序员在代码中捕获发生的异常,自行处理
-
throw
将发生的异常抛出,交给调用者处理,最顶级的处理者就时JVM
4.2.3示意图
try{
//代码可能有异常
}catch(Exception e){
//捕获到异常
//1.当异常发生时
//2.系统将异常封装成Exception对象 e,传递给catch
//3.得到异常对象后,程序员,自己处理
//4.注意:如果没有发生异常catch代码块不执行
}finally{
//1.不管try代码块是否有异常发生,始终要执行finally
//2.所以,通常将释放资源的代码,放在finally。
}
异常发生时:
4.2.4 try-catch 方式处理异常-注意事项
- 如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
- 如果异常没有发生,则顺序执行try的代码块,不会进入到catch代码块
- 如果希望不管异常发布发生,都执行某段代码(比如关闭连接,释放资源等)则使用如下代码-finally
public class TryCatchDetail {
public static void main(String[] args) {
//ctrl+alt+t
//1.如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
//2.如果异常没有发生,则顺序执行try的代码块,不会进入到catch代码块
//3.如果希望不管异常发布发生,都执行某段代码(比如关闭连接,释放资源等)则使用如下代码-finally
try {
String str = "韩顺平";
int a = Integer.parseInt(str);
System.out.println("数字a" + a);
} catch (NumberFormatException e) {
System.out.println("异常信息:" + e.getMessage());
}finally {
System.out.println("finally代码执行");
}
System.out.println("继续执行");
}
}
- 可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointerException在前),如果发生异常,只会匹配一个catch,案例演示
public class TryCatchDetail02 {
//4.可以有多个catch语句,捕获不同的异常(进行不同的业务处理),
//要求父类异常在后,子类异常在前,比如(Exception在后,NullPointerException在前),
//如果发生异常,只会匹配一个catch,案例演示
public static void main(String[] args) {
try {
Person person = new Person();
person = null;
System.out.println(person.getName());//NullPointerException
int n1 = 10;
int n2 = 0;
int num = n1 / n2;//ArithmeticException
} catch (NullPointerException e) {
System.out.println("空指针异常" + e.getMessage());
}catch (ArithmeticException e){
System.out.println("数学格式异常" + e.getMessage());
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
System.out.println("finally");
}
}
}
class Person {
private String name = "覃波";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 可以进行 try-finally 配合使用, 这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。应用场景,就是执行一段代码,不管是否发生异常, 都必须执行某个业务逻辑。
public class TryCatchDetail03 {
/*可以进行 try-finally 配合使用, 这种用法相当于没有捕获异常,
因此程序会直接崩掉/退出。应用场景,就是执行一段代码,
不管是否发生异常, 都必须执行某个业务逻辑 */
public static void main(String[] args) {
try {
int n1 = 10;
int n2 = 0;
int sum = n1 / n2;
}finally {
System.out.println("finally执行了");
}
System.out.println("程序继续执行");
}
}
4.2.5 异常处理课堂练习
- 输出什么?
//输出什么?
public class TryCatchExercise01 {
public static void main(String[] args) {
System.out.println(method());
}
public static int method(){
try {
String[] names = new String[3];
if (names[1].equals("tom")){
System.out.println(names[1]);
}else {
names[3] = "qinBo";
}
return 1;
}catch (ArrayIndexOutOfBoundsException e) {
return 2;
}catch (NullPointerException e){
return 3;
}finally {
return 4;
}
}
}
- 输出什么?
public class TryCatchExercise02 {
public static void main(String[] args) {
System.out.println(method());
}
public static int method() {
int i = 1;
try {
i++;//2
String[] names = new String[3];
if (names[i].equals("tom")) {
System.out.println(names[1]);
} else {
names[3] = "qinBo";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return ++i;//3
} finally {
return ++i;//4
}
}
}
public class TryCatchExercise03 {
public static void main(String[] args) {
System.out.println(method());
}
public static int method() {
int i = 1;
try {
i++;//2
String[] names = new String[3];
if (names[i].equals("tom")) {
System.out.println(names[1]);
} else {
names[3] = "qinBo";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return ++i;//i = 3 ->保存临时变量 temp = 3 ②
} finally {
++i;//i = 4
System.out.println("i=" +i);//i = 4 ①
}
}
}
- 如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止
public class TryCatchExercise04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = 0;
String inputStr = "";
while (true){
System.out.println("请输入一个整数");
inputStr = scanner.next();
try {
num = Integer.parseInt(inputStr);//这里可能抛出异常
break;
} catch (NumberFormatException e) {
System.out.println("输入的不是一个整数");
}
}
System.out.println("你输的是:" + num);
}
}
4.2.6Throw异常处理
- 基本介绍
public class ThrowsDetail01 {
public static void main(String[] args) {
}
public void f2() throws Exception {
//创建了一个文件流对象
//1.这里的异常是一个 FilerNotFoundException 编译异常
//2.使用前面讲过的 try-catch-finally
//3.使用throw,抛出异常,让调用f2方法的调用者(方法)去处理
//4.throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
//5.throw 关键字后,也可以是异常列表,即可以抛出多个异常。
FileInputStream fileInputStream = new FileInputStream("d://aa.txt");
}
}
-
注意事项和细节
-
对于编译异常,程序中必须处理,比如try-catch 或者 throws
-
对于运行时异常,程序中如果没有处理,默认的就是throw处理方式
-
子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型
-
在throws过程中,如果有方法try-catch,就相当于处理异常,就可以不必throws
public class ThrowsDetail02 {
public static void main(String[] args) throws Exception {
f1();
}
//①
public static void f1()/* throws Exception*/ {
//1.对于编译异常,程序中必须处理,比如try-catch 或者 throws
//2.对于运行时异常,程序中如果没有处理,默认的就是throw处理方式
int n1 = 10;
int n2 = 0;
double num = n1 / n2;
}
//补充②
public static void f2(){
//调用f3()报错
//解读
//因为f3()方法抛出的是一个编译异常
//f2调用这个异常,就要在f2()处理这个编译异常
//f2()可以抛出throw处理也可以try-catch处理
try {
f3();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//③
public static void f4(){
//在f4()调用了f5()方法是可以的
//因为 f5();抛出的是运行异常 而java中,并不要求程序员显示处理这个异常,因为有默认的处理机制
f5();
}
public static void f5() throws ArithmeticException{
}
}
public static void f3() throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream("d://aa.txt");
}
}
//3.子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要
//么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型
//4在throws过程中,如果有方法try-catch,就相当于处理异常,就可以不必throws
class Father {
public void method() throws RuntimeException {
}
}
class Son extends Father {
@Override
public void method() throws NullPointerException {
}
4.2.7 自定义异常
- 基本概念
当程序中出现了某些"错误”,但该错误信息并没有在Throwable子类中描
述处理,这个时候可以自己设计异常类,用于描述该错误信息。
-
自定义异常步骤
1)定义类:自定义异常类名(程序员自己写)继承Exception?或RuntimeException
2)如果继承Exception,属于编译异常
3)如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)
代码演示:
public class CustomException01 {
public static void main(String[] args) /*throws Exception */{
int age = 17;
if (!(age<120 && age>18)){
throw new AgeException("年龄须在18-120之间");
}
System.out.println("你的年龄范围正确");
}
}
//自定义一个异常
//1.一般情况下,我们自定义异常是继承RuntimeException
//2.即把自定义异常 做成 运行时异常 好处是我们可以使用默认的处理机制
class AgeException extends RuntimeException{
public AgeException(String message) {//构造器
super(message);
}
}
4.2.8 throw 和 throws 的区别
练习题:
4.2.9本章作业题
public class HomeWork01 {
public static void main(String[] args) {
//输出时进行配置初始化
//先验证接受到的参数个数是否正确 两个参数
try {
if (args.length != 2){
throw new ArrayIndexOutOfBoundsException("参数个数不对");//可能抛出参数不对 ArrayIndexOutOfBoundsException
}
int n1 = Integer.parseInt(args[0]);
int n2 = Integer.parseInt(args[1]);//可能抛出 类型转换错误 NumberFormatException
double res = EcmDef.cal(n1,n2);//该方法可能抛出 数字格式错误 ArithmeticException
System.out.println("计算结果是:" + res);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage());
}catch (NumberFormatException e){
System.out.println("参数格式不正确" +e.getMessage());
}catch (ArithmeticException e){
System.out.println("传入参数格式不对" + e.getMessage());
}
}
}
class EcmDef {
public static double cal(int n1, int n2) {
return n1/n2;
}
}
我亦无他,惟手熟尔