【前提引入-成员内部类】
成员内部类是定义在外部类的成员位置上,并且没有static修饰
//Outer是外部类
public class Outer{
//Inner是成员内部类
class Inner{
}
}
可以直接访问外部类的所有成员,包括 private
可以添加任意访问修饰符(public,protected,default默认,private),因为它的地位就是一个成员。
作用域:和外部类的其他成员一样,为整个类体
成员内部类访问外部类成员:直接访问
public class Outer {
private String name = "外部类";
public class Inner{
public void show(){
//访问的外部类成员name
System.out.println(name);
}
}
}
外部类访问成员内部类:创建对象再访问,且可以访问成员内部类的所有成员(包括private)
public class Outer {
public class Inner{
//设置为私有
private String name = "内部类";
}
public void show(){
//外部类访问内部类成员
//1. 先创建内部类
Inner inner = new Inner();
//2. 再访问内部类成员
System.out.println(inner.name);
}
}
外部其他类访问成员内部类的两种方式:
创建外部类对象再创建成员内部类
public class Outer {
public class Inner{
}
}
class MyTest{
public static void main(String[] args) {
Outer outer = new Outer();
//创建内部类
Outer.Inner inner1 = outer.new Inner();
//也可以合做一句来写
Outer.Inner inner2 = new Outer().new Inner();
}
}
在外部类中定义方法返回成员内部类实例对象
public class Outer {
public class Inner{
}
public Inner createInnerInstance() {
return new Inner();
}
}
class MyTest{
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner innerInstance1 = outer.createInnerInstance();
//上述两句等价于:
Outer.Inner innerInstance2 = new Outer().createInnerInstance();
}
}
如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则
,如果想访问外部类成员,则可以使用 外部类名.this.成员 来访问外部类成员。
public class Outer {
private String name = "外部类";
public class Inner{
private String name = "成员内部类";
public void show() {
//就近原则 -> 访问内部类,输出:成员内部类
System.out.println(name);
//访问外部类成员变量name,输出:外部类
System.out.println(Outer.this.name);
}
}
}
【核心代码】
编写 Outer 类:
public class Outer {
/**
* 成员内部类:Inner
*/
class Inner {
/**
* 内部类的成员变量
*/
public String name = "普通内部类";
/**
* 内部类的成员方法
*/
public void show() {
System.out.println("====调用Inner成员方法====");
}
}
/**
* 测试外部类调用内部类的成员变量和成员方法
*/
public void show() {
Inner inner = new Inner();
System.out.println("调用inner成员变量:" + inner.name);
inner.show();
}
/**
* 主启动类
* @param args 参数
*/
public static void main(String[] args) {
new Outer().show();
}
}
【运行流程】
【前提引入-异常简单说明】
1️⃣ 什么是异常
java语言中,将程序执行中发生的不正常情况称为"异常"。
⚠️开发过程中的语法错误和逻辑错误不是异常
2️⃣异常分类
栈溢出StackOverFlowError
和内存不足Out of Memory
。运行时异常
:程序运行时发生的异常编译时异常
:编译时编译器检查出的异常3️⃣ 注意事项
4️⃣ 常见的运行时异常
NullPointerException 空指针异常
当应用程序试图在需要对象的地方使用null时,抛出该异常。例如:
public class ExceptionTest {
public static void main(String[] args) {
//声明为null,未实例化
String str = null;
//调用str的toString方法,则会抛出NullPointerException异常
System.out.println(str.toString());
}
}
ArithmeticException 数学运算异常
当出现异常的运算条件时,抛出此异常。例如:
public class ExceptionTest {
public static void main(String[] args) {
// 很明显 10/0 是一个错误的数学运算,因此抛出ArithmeticException异常
System.out.println(10/0);
}
}
ArrayIndexOutOfBoundsException 数组下标越界异常
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。例如:
public class ExceptionTest {
public static void main(String[] args) {
// arr的数组长度为3,则索引范围是 [0,2]
int[] arr = {1,2,3};
// 索引为3,不在 [0,2] 范围内,因此抛出异常ArrayIndexOutOfBoundsException
System.out.println(arr[3]);
}
}
ClassCastException 类型转换异常
当我们用子类对象去强制转换父类对象就会报错,同样会抛出此异常。例如:
public class ExceptionTest {
public static void main(String[] args) {
//所有类都默认直接或间接继承了str,Object是顶级父类
Object object = new Object();
//因此这是在把一个父类实例对象转为子类,会抛出异常ClassCastException
String str = (String)object;
}
}
NumberFormatException 数字格式不正确异常
当应用程序试图将字符串转换成一种数值类型,但是该字符串不能转换为适当格式时,抛出该异常 --> 使用异常我们可以确保输入是满足条件的数字
public class ExceptionTest {
public static void main(String[] args) {
String str = "狐狸半面添";
//parseInt方法:试图将str转为整型
//但很明显,我们定义的 str 无法转为整型,因此抛出异常NumberFormatException
int num = Integer.parseInt(str);
}
}
我们还可以查看一下Integer类的parseInt方法源码,看下是否当类型不符合时真的抛出此异常:
在这段源码中应当是可以看到大量地方提到了 throw new NumberFormatException()
public static int parseInt(String s, int radix) throws NumberFormatException{
/*
* WARNING: This method may be invoked early during VM initialization
* before IntegerCache is initialized. Care must be taken to not use
* the valueOf method.
*/
if (s == null) {
throw new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}
int result = 0;
boolean negative = false;
int i = 0, len = s.length();
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}
5️⃣ 异常处理
异常处理就是当异常发生时,对异常的处理方式。
try-catch-finally
程序员在代码中捕获发生的异常,自行处理
public class ExceptionTest {
public static void main(String[] args) {
// 很明显 10/0 是一个错误的数学运算,因此抛出ArithmeticException异常
try {
System.out.println(10/0);
} catch (Exception e) {
System.out.println("这里是用来捕获发生的异常");
//创建一个异常类,并抛出该异常给方法的调用者来处理
throw new RuntimeException(e);
}finally {
System.out.println("这里通常是进行资源释放,也可以不写finally代码块");
}
}
}
throws
将发生的异常抛出,交给调用者来处理,最顶级的处理者就是 JVM 虚拟机
public class ExceptionTest {
// throws Exception:发生异常时则抛出异常给该方法的调用者处理
public static void main(String[] args) throws Exception {
// 很明显 10/0 是一个错误的数学运算,因此抛出ArithmeticException异常
System.out.println(10 / 0);
}
}
6️⃣ 自定义异常类
基本概念
自定义异常类的步骤
定义一个类:必须继承 Excpetion类
或 RuntimeExcpetion类
如果继承 Exception,属于编译异常;
如果继承 RuntimeException,属于运行异常。
一般情况下爱,我们自定义异常是继承 RuntimeExcpetion,即把自定义异常做成运行时异常类,好处是可以使用默认的处理机制——throws方式处理
自定义异常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法。
示例演示:
public class MyException extends RuntimeException{
public MyException() {
}
public MyException(String message) {
super(message);
}
}
【题目要求与核心代码】
通过继承Exception类,编写一个DangerException类:该异常类有构造方法,该构造方法使用super调用父类构造方法,使用字符串:“属于危险品!”,对父类变量message进行初始化。
/**
* 异常类
*/
public class DangerException extends Exception{
public DangerException() {
super("属于危险品!");
}
}
编写商品类:Goods,该类包含以下成员:
/**
* 商品类
*/
public class Goods {
/**
* 商品名称
*/
private String name;
/**
* 表示该商品是否为危险品,默认初始化为 false
* true:为危险品
* false:不是危险品
*/
private boolean isDanger;
public Goods(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isDanger() {
return isDanger;
}
public void setDanger(boolean danger) {
isDanger = danger;
}
}
编写一个Machine类,该类的方法checkBag(Goods goods)。当发现参数goods是危险品时,即:goods的isDanger属性为true时,该方法抛出DangerException异常的对象。
import java.util.ArrayList;
import java.util.Arrays;
/**
* 定义工具类,扫描是否为危险品
*/
public class Machine {
/**
* 危险商品名集合
*/
public final static ArrayList<String> DangerGoods = new ArrayList<String>(Arrays.asList("炸药","硫酸","硫磺"));
/**
* 检查商品是否为危险品
*
* @param goods 检查的商品
*/
public static void checkBag(Goods goods) throws DangerException {
if(DangerGoods.contains(goods.getName())){
throw new DangerException();
}
}
}
编写主类Check,在其main方法中创建创建商品对象,并使用Machine对象检查商品。
/**
* 主类:进行检查
*/
public class Check {
public static void main(String args[]) {
//商品名称
String[] name = {"苹果", "炸药", "西服", "硫酸", "手表", "硫磺"};
Goods[] goods = new Goods[name.length];
//创建商品对象
for (int i = 0; i < name.length; i++) {
goods[i] = new Goods(name[i]);
}
//检查商品
for (int i = 0; i < name.length; i++) {
try {
Machine.checkBag(goods[i]);
System.out.println(goods[i].getName() + ",检查通过");
} catch (DangerException e) {
System.out.println(goods[i].getName() + e.getMessage());
System.out.println(goods[i].getName() + ",被禁止!");
}
//换行分割
System.out.println();
}
}
}
程序输出如下:
苹果,检查通过
炸药属于危险品!
炸药,被禁止!
西服,检查通过
硫酸属于危险品!
硫酸,被禁止!
手表,检查通过
硫磺属于危险品!
硫磺,被禁止!
【运行流程】