目录
3. ArrayIndexOutOfBoundsException
注解(Annotation)
- 注解也称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息
- 和注释一样,注解不影响程序逻辑,但不同于注释,注解可以被编译或运行,相当于嵌入再代码中的补充信息
- 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据更重要的角色,例如用来配置应用程序的任何切面,代替javaEE旧版本中所遗留的繁冗代码和XML配置等
- 使用Annotation时要在前面加上@符号,并把Annotation当成以一个修饰符使用
三个基本的Annotation:
- @override:重写父类方法,只能注解方法
- @Deprecated:表示某个程序元素(类、方法等)已过时
- @SuppressWarnings:抑制编译器警告
@override
- 方法前写了@override,编译器会进行语法校验,编译器会判断该方法是否是重写方法,如果没有构成重写,编译器就会报错
- @Target是修饰注解的注解,称为元注解
在idea中快速定位到override的源码:ctrl + alt + B
@Deprecated
- @Deprecated修饰某个元素,表示这个元素已经过时了,不推荐使用,但还是能用
- @Deprecated源码如下:
- @Deprecated可以做版本升级过渡时使用
@SuppressWarnings
- 当我们不希望看到一些警告信息时,可以使用@Suppress Warnings{}注解来抑制警告信息,即不显示这些警告信息
- 在{}中可以写入希望抑制(不显示)的警告信息,{}中是一个字符串数组,如all是抑制所有警告
- @Suppress Warnings作用范围和放置的位置有关,通常可以放在语句、方法、类上
package com.lili.Annotation_;
import java.util.ArrayList;
import java.util.List;
public class SuppressWarnings_ {
//1.当我们不希望看到一些警告信息时,可以使用@SuppressWarnings注解来抑制警告信息
//2.在{""}中可以写入希望抑制(不显示)的警告信息
@SuppressWarnings({"unchecked", "rawtypes", "unused"})
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("你好");
list.add("hi");
int n;
}
}
元注解(了解)—— 修饰注解的注解
元注解的类型:
- @Retention 指定注解的作用时间范围,三种:SOURCE, CLASS, RUNTIME
- @Target 指定注解可以在哪些地方使用
- @Documented 指定注解是否会在javadoc体现
- @Inherited 子类会继承父类注解
静态属性、内部类、枚举类的练习
test1:看看会输出什么?
package com.homework.chapter10;
import java.awt.*;
public class Homework01 {
public static void main(String[] args) {
Car car = new Car(); //1
Car car1 = new Car(11); //2
System.out.println(car);
System.out.println(car1);
}
}
class Car{
double price = 10;
static String color = "white";
@Override
public String toString() {
return "price = " + price +
" color = " + color ;
}
public Car() {
this.price = 9;
this.color = "red";
}
public Car(double price) {
this.price = price;
}
}
语句1 new Car() 时,会先进行类加载,进行静态属性的初始化执行静态代码块(如果有的话) ,类变量color初始化为白色;再执行构造器,price = 9, 类变量color = red
语句2 ,car1指向一个新的Car对象,因为静态属性和静态代码块随类加载而执行一次且仅一次,所以直接执行构造器,price = 11,类变量color是共享的,color = red
所以输出 :
price = 9.0 color = red
price = 11.0 color = red
test2
/**
* 1.在Frock类中声明私有的静态属性currentNum[int类型],初始值为100000,作为衣服出厂序列号起始值
* 2.声明公有的静态方法getNextNum,作为生成上衣唯一序列号的方法,每调用一次,将currentNUM加100,并作为返回值
* 3.在main方法中,分两次调用getNextNum方法,获取序列号并打印输出
* 4.在Frock类中声明serialNumber(序列号)属性,并提供对应的get方法
* 5.在Frock类的构造器中,通过调用getNextNum方法为Frock对象获取唯一序列号,赋给serialNumber属性
* 6.在main方法中,分别创建3个Frock对象,并打印三个对象的序列号,验证是否为按100递增
*/
package com.homework.chapter10;
public class Homework02 {
public static void main(String[] args) {
System.out.println("第一次调用getNextNum():" + Frock.getNextNum());
System.out.println("第二次调用getNextNum():" + Frock.getNextNum());
Frock frock = new Frock();
Frock frock1 = new Frock();
Frock frock2 = new Frock();
System.out.println(frock.getSerialNumber());
System.out.println(frock1.getSerialNumber());
System.out.println(frock2.getSerialNumber());
}
}
class Frock {
private int serialNumber;
private static int currentNum = 100000;
public Frock() {
this.serialNumber = getNextNum();
}
public int getSerialNumber() {
return serialNumber;
}
public static int getNextNum() {
currentNum += 100;
return currentNum;
}
}
test3
/**
* 1.计算器接口具有work方法,功能是运算,有一个手机类Cellphone,
* 定义方法testWork测试计算功能,调用计算接口的work方法
* 2.要求调用Cellphone对象的testWork方法,使用匿名内部类
*/
package com.homework.chapter10;
import javafx.scene.control.Cell;
public class Homework04 {
public static void main(String[] args) {
//第一个实参编译类型是ICalculate,运行类型是匿名内部类
Cellphone.testWork(new ICalculate() {
@Override
public double work(double a, double b) {
return a + b;
}
},2, 6);
}
}
interface ICalculate {
//接口中的方法,除了抽象方法,还可以有static方法、default方法、private方法
double work(double a, double b);
}
class Cellphone {
public static void testWork(ICalculate cal, double a, double b) {
System.out.println("计算的结果是:" + cal.work(a, b));
}
}
test4
/**
* 1.编一个类A,在类中定义局部内部类B,B中有一个私有常量name,show()打印常量name
* 2.进阶:A中也定义一个私有常量name,在show()打印常量name
*/
package com.homework.chapter10;
public class Homework05 {
public static void main(String[] args) {
//每new一个对象,就会执行一次普通代码块
A a = new A();
}
}
class A {
private String name = "keddy";
{
System.out.println("A的普通代码块...");
//局部内部类
class B {
private String name = "Jerry";
void show() {
System.out.println("name = " + name);
//如果外部类的属性和局部内部类的属性重名,
//则访问外部类属性用:外部类.this.属性
System.out.println("A的name = " + A.this.name);
}
}
B b = new B();
b.show();
}
}
test5
package com.homework.chapter10;
import com.lili.houserent.domain.House;
import com.lili.interface_.Phone;
public class Homework06 {
public static void main(String[] args) {
Person tang = new Person("唐僧", VehiclesFactory.getHorse());
tang.passRiver();
tang.commonSituation();
tang.fireMountain();
}
}
interface Vehicles {
void work();
}
class Horse implements Vehicles {
@Override
public void work() {
System.out.println("一般情况下,骑马赶路...");
}
}
class Boat implements Vehicles {
@Override
public void work() {
System.out.println("遇到大河,乘船过河...");
}
}
class Plane implements Vehicles {
@Override
public void work() {
System.out.println("遇到火焰山,乘飞机过路...");
}
}
class VehiclesFactory {
//单例设计模式,饿汉式
private static Boat boat = new Boat();
private static Horse horse = new Horse();
private static Plane plane = new Plane();
private VehiclesFactory() {
}
public static Vehicles getBoat() {
return boat;
}
public static Vehicles getHorse() {
return horse;
}
public static Vehicles getPlane() {
return plane;
}
}
class Person {
private String name;
private Vehicles vehicles;
public Person(String name, Vehicles vehicles) {
this.name = name;
this.vehicles = vehicles;
}
public void passRiver() {
if(!(vehicles instanceof Boat)) {
vehicles = VehiclesFactory.getBoat();
}
vehicles.work();
}
public void commonSituation() {
if (!(vehicles instanceof Horse)) {
vehicles = VehiclesFactory.getHorse();
}
vehicles.work();
}
public void fireMountain() {
if(!(vehicles instanceof Plane)) {
vehicles = VehiclesFactory.getPlane();
}
vehicles.work();
}
}
test6
package com.homework.chapter10;
public class Homework07 {
public static void main(String[] args) {
Car car = new Car(26);
Car car1 = new Car(41);
Car car2 = new Car(0);
car.getAir().flow();
car1.getAir().flow();
car2.getAir().flow();
}
}
class Car {
private double temperature;
public Car(double temperature) {
this.temperature = temperature;
}
//成员内部类
class Air {
void flow() {
if (temperature > 40) {
System.out.println("现在温度" + temperature + "℃ 吹冷气...");
} else if (temperature < 0) {
System.out.println("现在温度" + temperature + "℃ 吹暖气...");
} else {
System.out.println("现在温度" + temperature + "℃ 空调关闭...");
}
}
}
public Air getAir() {
return new Air();
}
}
test7
package com.homework.chapter10;
import java.util.Scanner;
public class Homework08 {
public static void main(String[] args) {
Color color = Color.GREEN;
switch (color) {
case RED:
System.out.println("匹配到红色");
break;
case BLUE:
System.out.println("匹配到蓝色");
break;
case GREEN:
System.out.println("匹配到绿色");
break;
case BLACK:
System.out.println("匹配到黑色");
break;
case YELLOW:
System.out.println("匹配到黄色");
break;
}
color.show();
}
}
interface IA{
void show();
}
enum Color implements IA{
RED(255, 0, 0), GREEN(0, 255, 0),
BLUE(0,0, 255), BLACK(0, 0, 0),
YELLOW(255, 255, 0);
private double redValue;
private double greenValue;
private double blueValue;
Color(double redValue, double greenValue, double blueValue) {
this.redValue = redValue;
this.greenValue = greenValue;
this.blueValue = blueValue;
}
@Override
public void show() {
System.out.println(this.name() + ": " + this.toString());
}
@Override
public String toString() {
return "redValue=" + redValue +
", greenValue=" + greenValue +
", blueValue=" + blueValue +
'}';
}
}
异常(Exception)
- 程序执行中发生的不正常情况称为异常,开发过程中的语法错误和逻辑错误不是异常
- 如果程序员认为一段代码可能出现异常,可以用try-catch异常处理机制来解决,保证程序的健壮性
- 程序执行中发生的异常事件分为两种:Error(错误)和Exception
1.Error(错误):java虚拟机无法解决的严重问题。如:jvm系统内部错误、资源耗尽等严重情况。比如:StackOverflowError[栈溢出]和OOM(out of memory),Error是严重错误,程序会崩溃。
2.Exception :其它问题,可以使用针对性的代码处理。Exception分为两类:运行时异常和编译时异常。
选中可能出现异常的代码-->快捷键ctrl + alt + t -->选择try - catch (idea集成开发工具)
如果进行异常处理,程序即使出现异常,程序还是继续运行
package com.lili.exception_;
public class Exception01 {
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;
try {
int res = num1 / num2;
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("程序继续...");
}
}
- 异常分为两大类,编译时异常和运行时异常
- 运行时异常,编译器检查不出。一般指编程时的逻辑错误,是程序员应该避免其出现的异常
- 对于运行时异常,可以不做处理,因为这类异常很普遍,若全处理可能会影响程序的可读性和运行效率
- 编译时异常,是编译器要求必须处理的异常
五大运行时异常
1. NullPointerException
空指针异常:当应用程序试图在需要对象的地方使用null时,抛出该异常
2. ArithmeticException
数学运算异常
3. ArrayIndexOutOfBoundsException
数组下标越界异常,索引为负或大于等于数组长度
4. ClassCastException
类型转换异常 ,试图将对象强制转换为不是实例的子类
5. NumberFormatException
数字格式不正确异常
异常处理方式
1. try-catch-finally
程序员在代码中捕获发生的异常,自行处理
try{
可能有异常的代码
}catch(Exception e){
捕获到异常,系统将异常封装成Exception对象传递给catch,得到异常对象后,程序员自己处理。注意:如果没有发生异常,catch代码块不会执行
}finally{
不管代码块是否发生异常,都要执行finally,所以,通常将释放资源的代码放在finally
}
2. throws
将发生的异常抛出,交给调用者来处理,最顶级的处理者是JVM
如果没有显示的使用try-catch或throws处理异常,默认使用throws
try-catch细节
1. 如果发生了异常,try中异常发生后面的代码不会执行,直接进入到catch块
2. 如果异常没有发生,则顺序执行try的代码块,不会进入到catch
3. 如果希望不管是否发生异常都执行,比如关闭连接、释放资源等,就写到finally中
4. 可以有多个catch语句捕获异常,要求子类异常写在前面,父类异常写在后面
package com.lili.exception_;
public class TryCatchDetail {
public static void main(String[] args) {
try {
String name = null;
System.out.println(name.length());
String str = "12";
int num = Integer.parseInt(str);
System.out.println(num);
} catch (NullPointerException e) {
System.out.println("空指针异常=" + e.getMessage());
} catch (NumberFormatException e) {
System.out.println("算术异常=" + e.getMessage());
} catch (Exception e) {
System.out.println("异常信息=" + e.getMessage());
}
}
}
5.可以进行try-finally配合使用,相当于没有捕获异常,因此发生异常程序直接崩溃
应用场景:执行一段代码,不管是否发生异常,都必须执行某个业务逻辑
try-catch小练习
Exercise1:看看会输出什么?
package com.lili.exception_;
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")){ //①NullPointerException异常
System.out.println(names[1]);
}else{
names[3] = "lulu";
}
return 1;
}catch(ArrayIndexOutOfBoundsException e){
return 2;
}catch (NullPointerException e){ //②捕获异常
return 3;
}finally { //③最后一定会执行finally
return 4;
}
}
}
answer: 4
Exercise2
package com.lili.exception_;
public class TryCatchException02 {
public static void main(String[] args) {
System.out.println(method()); //4
}
public static int method(){
int i = 1;
try{
i++; //i=2
String[] names = new String[3];
if(names[1].equals("tom")){ //NullPointerException
System.out.println(names[1]);
}else{
names[3] = "lulu";
}
return 1;
}catch(ArrayIndexOutOfBoundsException e){
return 2;
}catch (NullPointerException e){
return ++i; //return 3,i=3
}finally {
return ++i; //return 4,i=4
}
}
}
answer: 4
Exercise3
package com.lili.exception_;
public class TryCatchExercise03 {
public static void main(String[] args) {
System.out.println(method()); //3
}
public static int method(){
int i = 1;
try{
i++; //i=2
String[] names = new String[3];
if(names[1].equals("tom")){ //NullPointerException
System.out.println(names[1]);
}else{
names[3] = "lulu";
}
return 1;
}catch(ArrayIndexOutOfBoundsException e){
return 2;
}catch (NullPointerException e){
return ++i; //i=3先不会执行return ->返回值保存到临时变量temp=3,先执行finally
}finally {
++i; //i=4
System.out.println("i = " + i); //i = 4
}
}
}
answer:
i = 4
3
Exercise4
编程:如果用户输入的不是一个整数,就提示反复输入,直到输入一个整数为止
package com.lili.exception_;
import java.util.Scanner;
/**
* s如果用户输入的不是一个整数,就提示反复输入,直到输入一个整数为止
*/
public class TryCatchExercise4 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String inputStr = "";
int num = 0;
while (true) {
System.out.print("请输入一个整数:");
inputStr = scanner.next();
try {
num = Integer.parseInt(inputStr);
break; //如果没抛出异常,就会执行break;
} catch (NumberFormatException e) {
System.out.println("输入不是一个整数");
}
}
System.out.println("输入的整数:" + num);
}
}
throws异常处理
1.如果一个方法可能出现异常,但是不能确定如何处理这种异常,则此方法应该显示地声明抛出异常,表明该方法将不对异常进行处理,而由该方法的调用者处理
2.throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
3.可以用try-catch-finally,也可以用throws抛出异常,让调用者去处理(二选一)
4.throws语句可以声明抛出异常的列表,逗号分隔
throws细节:
1.对于编译异常,程序必须处理
2.对于运行异常,程序如果没有处理,默认用Throws处理
3.子类重写父类,子类抛出的异常要么和父类一致,要么是父类抛出异常类型的子类
public void f1() throws FileNotFoundException {
//这里的异常是编译异常FileNotFoundException
FileInputStream fis = new FileInputStream("D://a.txt");
}
public static void f2() throws FileNotFoundException{
/*
因为f3()方法抛出的是一个编译异常,编译异常必须处理,
f2()必须处理这个编译异常,在f2()中,要么try-catch-finally,
要么继续throws这个编译异常
*/
f3();
}
public static void f3() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("D://a.txt");
}
public static void f4() {
/*
因为f5()方法抛出的是一个运行异常,运行异常可以不必处理,
java中有默认处理机制,
所以不报错
*/
f5();
}
public static void f5() throws ArithmeticException {
}
自定义异常
当程序出现了一些“错误”,但该错误信息并没有在Throwable子类中描述处理,这时可以自定义异常类,用于描述错误信息
1.定义类:自定义异常类名,继承Exception或RuntimeException
2.如果继承Exception,属于编译异常
3.如果继承RuntimeException,属于运行异常
4.一般继承RuntimeException运行异常,好处是有默认的处理机制
package com.lili.customexception_;
/**
*要求age在18-120,否则抛出异常
*/
public class CustomException {
public static void main(String[] args) {
int age = 12;
if(!(age >= 18 && age <= 120)) {
//可以通过构造器设置信息
throw new AgeException("年龄需在18~120之间"); //注意这里是throw + 异常对象
}
System.out.println("年龄正确");
}
}
//自定义一个异常,age在18-120
class AgeException extends RuntimeException{
public AgeException(String message){
super(message);
}
}
throw和throws区别
- throws是异常处理的一种方式,throw是手动生成异常对象的关键字
- throws位于方法声明处,throw位于方法体内
- throws后面跟异常类型,throw后面跟异常对象
看看会输出什么?
package com.lili.throws_;
public class ThrowException {
public static void main(String[] args) {
try {
ReturnExceptionDemo.methodA();
} catch (Exception e) {
System.out.println(e.getMessage());
}
ReturnExceptionDemo.methodB();
}
}
class ReturnExceptionDemo{
static void methodA(){
try {
System.out.println("进入方法A");
throw new RuntimeException("制造异常");
} finally {
System.out.println("用A方法的finally");
}
}
static void methodB(){
try {
System.out.println("进入方法B");
return;
} finally {
System.out.println("用B方法的finally");
}
}
}
answer:
异常练习题
1. 编写应用程序EcmDef.java,接收命令行的两个参数,计算两数整除
2. 计算两数整除,用cal(int n1, int n2)
3. 对数据格式不正确、缺少命令行参数、除0进行异常处理
package com.lili.homework;
import java.util.Scanner;
public class homework01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//缺少命令行参数
try {
if(args.length != 2) {
throw new ArrayIndexOutOfBoundsException("命令行参数个数不对");
}
//接收命令行的两个参数
int n1 = Integer.parseInt(args[0]);
int n2 = Integer.parseInt(args[1]);
//除0异常
System.out.println(EcmDef.cal(n1, n2));
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage());
} catch (NumberFormatException e) {
System.out.println("参数格式不正确");
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
}
}
}
class EcmDef{
public static double cal(int n1, int n2){
return n1 / (double)n2;
}
}