当前位置: 首页 > news >正文

[从0开始学Java|第二十七天]IO(异常File)

异常

异常体系介绍

什么是异常?

异常体系

Error

Exception

总结

编译时异常和运行时异常

什么是编译时异常和运行时异常?

package com.itheima.a01myexception; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ExceptionDemo1 { public static void main(String[] args) throws ParseException { //编译时异常(在编译阶段,必须要手动处理,否则代码报错) String time = "2030年1月1日"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); Date date = sdf.parse(time); System.out.println(date); //运行时异常(在编译阶段是不需要处理的,是代码运行时出现的异常) int[] arr = {1,2,3,4,5}; System.out.println(arr[10]);//ArrayIndexOutOfBoundsException } }

总结

异常在代码中的两个作用

作用一

package com.itheima.a01myexception; public class ExceptionDemo2 { public static void main(String[] args) { /* 异常作用一:异常是用来查询bug的关键参考信息 异常作用二:异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况 */ Student[] arr = new Student[3];// null null null String name = arr[0].getName(); System.out.println(name); } }

作用二

package com.itheima.a01myexception; public class ExceptionDemo4 { public static void main(String[] args) { /* 异常作用一:异常是用来查询bug的关键参考信息 异常作用二:异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况 */ //1.创建学生对象 Student s1 = new Student(); //年龄:(同学) 18~40岁 s1.setAge(50);//就知道了50赋值失败 //选择1:自己悄悄处理 //选择2:打印在控制台上 } }
package com.itheima.a01myexception; public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { if(age < 18 || age > 40){ //System.out.println("年龄超出范围"); throw new RuntimeException(); }else{ this.age = age; } } public String toString() { return "Student{name = " + name + ", age = " + age + "}"; } }

异常的处理方式

JVM虚拟机默认处理异常的方式

例如:

package com.itheima.a01myexception; public class ExceptionDemo5 { public static void main(String[] args) { /* JVM默认处理异常的方式: 1. 把异常的名称,异常原因及异常出现的位置等信息输出在了控制台 2. 程序停止执行,异常下面的代码不会再执行了 */ System.out.println("狂踹瘸子那条好腿"); System.out.println(2/0);//算术异常 ArithmeticException System.out.println("是秃子终会发光"); System.out.println("火鸡味锅巴"); } }

自己处理(捕获异常)

格式和目的

例如:

(源代码)

package com.itheima.a01myexception; public class ExceptionDemo6 { public static void main(String[] args) { /* 自己处理(捕获异常) 格式: try { 可能出现异常的代码; } catch(异常类名 变量名) { 异常的处理代码; } 好处:可以让程序继续往下执行,不会停止 */ int[] arr = {1, 2, 3, 4, 5, 6}; try{ //可能出现异常的代码; System.out.println(arr[10]);//此处出现了异常,程序就会在这里创建一个ArrayIndexOutOfBoundsException对象 //new ArrayIndexOutOfBoundsException(); //拿着这个对象到catch的小括号中对比,看括号中的变量是否可以接收这个对象 //如果能被接收,就表示该异常就被捕获(抓住),执行catch里面对应的代码 //当catch里面所有的代码执行完毕,继续执行try...catch体系下面的其他代码 }catch(ArrayIndexOutOfBoundsException e){ //如果出现了ArrayIndexOutOfBoundsException异常,我该如何处理 System.out.println("索引越界了"); } System.out.println("看看我执行了吗?"); } }

(运行结果截图)

自己处理(捕获异常)灵魂四问

灵魂一问

灵魂一问:如果try中没有遇到问题,怎么执行

会把try里面所有的代码全部执行完毕,不会执行catch里面的代码

注意:
只有当出现了异常才会执行catch里面的代码

例如:

(源代码)

package com.itheima.a01myexception; public class ExceptionDemo7 { public static void main(String[] args) { /* 自己处理(捕获异常)灵魂四问: 灵魂一问:如果try中没有遇到问题,怎么执行? 会把try里面所有的代码全部执行完毕,不会执行catch里面的代码 注意: 只有当出现了异常才会执行catch里面的代码 */ int[] arr = {1, 2, 3, 4, 5, 6}; try{ System.out.println(arr[0]);//1 }catch(ArrayIndexOutOfBoundsException e){ System.out.println("索引越界了"); } System.out.println("看看我执行了吗?");//看看我执行了吗? } }

(运行结果截图)

灵魂二问

灵魂二问:如果try中可能会遇到多个问题,怎么执行?
会写多个catch与之对应

细节:
如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面

了解性:
在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开
表示如果出现了A异常或者B异常的话,采取同一种处理方案

例如:

(源代码)

package com.itheima.a01myexception; public class ExceptionDemo8 { public static void main(String[] args) { /* 自己处理(捕获异常)灵魂四问: 灵魂二问:如果try中可能会遇到多个问题,怎么执行? 会写多个catch与之对应 细节: 如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面 了解性: 在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开 表示如果出现了A异常或者B异常的话,采取同一种处理方案 */ //JDK7 int[] arr = {1, 2, 3, 4, 5, 6}; try{ System.out.println(arr[10]);//ArrayIndexOutOfBoundsException System.out.println(2/0);//ArithmeticException String s = null; System.out.println(s.equals("abc")); }catch(ArrayIndexOutOfBoundsException e) { System.out.println("索引越界了"); }catch(ArithmeticException e){ System.out.println("除数不能为0"); }catch(NullPointerException e){ System.out.println("空指针异常"); }catch (Exception e){ System.out.println("Exception"); } System.out.println("看看我执行了吗?"); } }

(运行结果截图)

灵魂三问

灵魂三问:如果try中遇到的问题没有被捕获,怎么执行?
相当于try...catch的代码白写了,最终还是会交给虚拟机进行处理。

例如:

(源代码)

package com.itheima.a01myexception; public class ExceptionDemo9 { public static void main(String[] args) { /* 自己处理(捕获异常)灵魂三问: 如果try中遇到的问题没有被捕获,怎么执行? 相当于try...catch的代码白写了,最终还是会交给虚拟机进行处理。 */ int[] arr = {1, 2, 3, 4, 5, 6}; try{ System.out.println(arr[10]);//new ArrayIndexOutOfBoundsException(); }catch(NullPointerException e){ System.out.println("空指针异常"); } System.out.println("看看我执行了吗?"); } }

(运行结果截图)

灵魂四问

灵魂四问:如果try中遇到了问题,那么try下面的其他代码还会执行吗?
下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体
但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理

例如:

(源代码)

package com.itheima.a01myexception; public class ExceptionDemo10 { public static void main(String[] args) { /* 自己处理(捕获异常)灵魂四问: 如果try中遇到了问题,那么try下面的其他代码还会执行吗? 下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体 但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理 */ int[] arr = {1, 2, 3, 4, 5, 6}; try{ System.out.println(arr[10]); System.out.println("看看我执行了吗?... try"); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("索引越界了"); } System.out.println("看看我执行了吗?... 其他代码"); } }

(运行结果截图)

总结

异常中的常见方法

Throwable的成员方法

快捷键:

ctrel + alt + T

例如:

(源代码)

package com.itheima.a01myexception; public class ExceptionDemo11 { public static void main(String[] args) { /* public String getMessage() 返回此 throwable 的详细消息字符串 public String toString() 返回此可抛出的简短描述 public void printStackTrace() 在底层是利用System.err.println进行输出 把异常的错误信息以红色字体输出在控制台 细节:仅仅是打印信息,不会停止程序运行 */ int[] arr = {1, 2, 3, 4, 5, 6}; try { System.out.println(arr[10]); } catch (ArrayIndexOutOfBoundsException e) { String message = e.getMessage(); System.out.println(message);//Index 10 out of bounds for length 6 String str = e.toString(); System.out.println(str);//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 6*//* e.printStackTrace(); } System.out.println("看看我执行了吗?"); //正常的输出语句 System.out.println(123); //错误的输出语句(而是用来打印错误信息) System.err.println(123); } }

(运行结果截图)

抛出异常

例如:

(源代码)

package com.itheima.a01myexception; public class ExceptionDemo12 { public static void main(String[] args) { /* throws:写在方法定义处,表示声明一个异常。告诉调用者,使用本方法可能会有哪些异常。 throw :写在方法内,结束方法。手动抛出异常对象,交给调用者。方法中下面的代码不再执行了。 需求: 定义一个方法求数组的最大值 */ int[] arr = null; int max = 0; try { max = getMax(arr); } catch (NullPointerException e) { System.out.println("空指针异常"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("索引越界异常"); } System.out.println(max); } public static int getMax(int[] arr)/* throws NullPointerException,ArrayIndexOutOfBoundsException*/{ if(arr == null){ //手动创建一个异常对象,并把这个异常交给方法的调用者处理 //此时方法就会结束,下面的代码不会再执行了 throw new NullPointerException(); } if(arr.length == 0){ //手动创建一个异常对象,并把这个异常交给方法的调用者处理 //此时方法就会结束,下面的代码不会再执行了 throw new ArrayIndexOutOfBoundsException(); } System.out.println("看看我执行了吗?"); int max = arr[0]; for (int i = 1; i < arr.length; i++) { if(arr[i] > max){ max = arr[i]; } } return max; } }

(运行结果截图)

综合练习

(源代码)

[Test]

package com.itheima.test1; import java.util.Scanner; public class Test { public static void main(String[] args) { /* 需求: 键盘录入自己心仪的女朋友姓名和年龄。 姓名的长度在 3 - 10之间, 年龄的范围为 18 - 40岁, 超出这个范围是异常数据不能赋值,需要重新录入,一直录到正确为止。 提示: 需要考虑用户在键盘录入时的所有情况。 比如:录入年龄时超出范围,录入年龄时录入了abc等情况 */ //1.创建键盘录入的对象 Scanner sc = new Scanner(System.in); //2.创建女朋友的对象 GirlFriend gf = new GirlFriend(); while (true) { //3.接收女朋友的姓名 try { System.out.println("请输入你心仪的女朋友的名字"); String name = sc.nextLine(); gf.setName(name); //4.接收女朋友的年龄 System.out.println("请输入你心仪的女朋友的年龄"); String ageStr = sc.nextLine(); int age = Integer.parseInt(ageStr); gf.setAge(age); //如果所有的数据都是正确的,那么跳出循环 break; } catch (NumberFormatException e) { System.out.println("年龄的格式有误,请输入数字"); //continue; } catch (RuntimeException e) { System.out.println("姓名的长度或者年龄的范围有误"); //continue; } } //5.打印 System.out.println(gf); } }

[GirlFriend]

package com.itheima.test1; public class GirlFriend { private String name; private int age; public GirlFriend() { } public GirlFriend(String name, int age) { this.name = name; this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { int len = name.length(); if(len < 3 || len > 10){ throw new RuntimeException(); } this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { if(age < 18 || age > 40){ throw new RuntimeException(); } this.age = age; } public String toString() { return "GirlFriend{name = " + name + ", age = " + age + "}"; } }

(运行结果截图)

自定义异常

alt + insert

(源代码)

[Test]

package com.itheima.test2; import java.util.Scanner; public class Test { public static void main(String[] args) { /* 需求: 键盘录入自己心仪的女朋友姓名和年龄。 姓名的长度在 3 - 10之间, 年龄的范围为 18 - 40岁, 超出这个范围是异常数据不能赋值,需要重新录入,一直录到正确为止。 提示: 需要考虑用户在键盘录入时的所有情况。 比如:录入年龄时超出范围,录入年龄时录入了abc等情况 */ //1.创建键盘录入的对象 Scanner sc = new Scanner(System.in); //2.创建女朋友的对象 GirlFriend gf = new GirlFriend(); while (true) { //3.接收女朋友的姓名 try { System.out.println("请输入你心仪的女朋友的名字"); String name = sc.nextLine(); gf.setName(name); //4.接收女朋友的年龄 System.out.println("请输入你心仪的女朋友的年龄"); String ageStr = sc.nextLine(); int age = Integer.parseInt(ageStr); gf.setAge(age); //如果所有的数据都是正确的,那么跳出循环 break; } catch (NumberFormatException e) { e.printStackTrace(); } catch (NameFormatException e) { e.printStackTrace(); }catch (AgeOutOfBoundsException e) { e.printStackTrace(); } } //5.打印 System.out.println(gf); } }

[GirlFriend]

package com.itheima.test2; public class GirlFriend { private String name; private int age; public GirlFriend() { } public GirlFriend(String name, int age) { this.name = name; this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { int len = name.length(); if(len < 3 || len > 10){ throw new NameFormatException(name + "格式有误,长度应该为:3~10"); } this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { if(age < 18 || age > 40){ throw new AgeOutOfBoundsException(age + "超出了范围"); } this.age = age; } public String toString() { return "GirlFriend{name = " + name + ", age = " + age + "}"; } }

[NameFormatException]

package com.itheima.test2; public class NameFormatException extends RuntimeException{ //技巧: //NameFormat:当前异常的名字,表示姓名格式化问题 //Exception:表示当前类是一个异常类 //运行时:RuntimeException 核心 就表示由于参数错误而导致的问题 //编译时:Exception 核心 提醒程序员检查本地信息 public NameFormatException() { } public NameFormatException(String message) { super(message); } }

[AgeOutOfBoundsException]

package com.itheima.test2; public class AgeOutOfBoundsException extends RuntimeException{ public AgeOutOfBoundsException() { } public AgeOutOfBoundsException(String message) { super(message); } }

(运行结果截图)

File

File的概述和构造方法

File的概述

File的构造方法

例如:

(源代码)

package com.itheima.a01myfile; import java.io.File; public class FileDemo1 { public static void main(String[] args) { //1 String str = "C:\\Users\\alienware\\Desktop\\a.txt"; File f1 = new File(str); System.out.println(f1);//C:\Users\alienware\Desktop\a.txt //2 String parent = "C:\\Users\\alienware\\Desktop"; String child = "a.txt"; File f2 = new File(parent,child); System.out.println(f2);//C:\Users\alienware\Desktop\a.txt File f3 = new File(parent + "\\" + child); System.out.println(f3);//C:\Users\alienware\Desktop\a.txt //3 File parent2 = new File("C:\\Users\\alienware\\Desktop"); String child2 = "a.txt"; File f4 = new File(parent2, child2); System.out.println(f4);//C:\Users\alienware\Desktop\a.txt } }

(运行结果截图)

总结

File的成员方法

(判断、获取)

例如:

【判断】

(源代码)

package com.itheima.a01myfile; import java.io.File; public class FileDemo2 { public static void main(String[] args) { //1.对一个文件的路径进行判断 File f1 = new File("D:\\aaa\\aaa.txt"); System.out.println(f1.isDirectory());//false System.out.println(f1.isFile());//true System.out.println(f1.exists());//true System.out.println("----------------------------------------"); //2.对一个文件夹的路径进行判断 File f2 = new File("D:\\aaa\\bbb"); System.out.println(f2.isDirectory());//true System.out.println(f2.isFile());//false System.out.println(f2.exists());//true System.out.println("----------------------------------------"); //3.对一个不存在的路径进行判断 File f3 = new File("D:\\aaa\\ccc.txt"); System.out.println(f3.isDirectory());//false System.out.println(f3.isFile());//false System.out.println(f3.exists());//false } }

(运行结果截图)

【获取】

(源代码)

package com.itheima.a01myfile; import java.io.File; public class FileDemo3 { public static void main(String[] args) { //length() File f1 = new File("D:\\aaa\\aaa.txt"); long l = f1.length(); System.out.println(l);//0 System.out.println("----------------------------------------"); //getAbsolutePath() File f2 = new File("D:\\aaa\\aaa.txt"); String str1 = f2.getAbsolutePath(); System.out.println(str1);//D:\aaa\aaa.txt File f3 = new File("myfile\\bbb"); String str2 = f3.getAbsolutePath(); System.out.println(str2);//D:\javaCode\oop-polymorphism\myfile\bbb System.out.println("----------------------------------------"); //getPath() File f4 = new File("D:\\aaa\\aaa.txt"); String str3 = f4.getPath(); System.out.println(str3);//D:\aaa\aaa.txt File f5 = new File("myfile\\bbb"); String str4 = f5.getPath(); System.out.println(str4);//myfile\bbb System.out.println("----------------------------------------"); //getName() File f6 = new File("D:\\aaa\\aaa.txt"); String str5 = f6.getName(); System.out.println(str5);//aaa.txt File f7 = new File("myfile\\bbb"); String str6 = f7.getPath(); System.out.println(str6);//myfile\bbb System.out.println("----------------------------------------"); //lastModified() File f8 = new File("D:\\aaa\\aaa.txt"); long l2 = f8.lastModified(); System.out.println(l2);//1771425864816 } }

(运行结果截图)

(创建、删除)

注意:

delete方法默认只删除文件和空文件夹,delete方法直接删除不走回收站

例如:

【创建】

(源代码)

package com.itheima.a01myfile; import java.io.File; import java.io.IOException; public class FileDemo4 { public static void main(String[] args) throws IOException { /* public boolean createNewFile() 创建一个新的空的文件 public boolean mkdir() 创建单级文件夹 public boolean mkdirs() 创建多级文件夹 public boolean delete() 删除文件、空文件夹 */ //1.createNewFile 创建一个新的空的文件 //细节1:如果当前路径表示的文件是不存在的,则创建成功,方法返回true // 如果当前路径表示的文件是存在的,则创建失败,方法返回false //细节2:如果父级路径是不存在的,那么方法会有异常IOException //细节3:createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件 File f1 = new File("D:\\aaa\\ddd"); boolean b1 = f1.createNewFile(); System.out.println(b1);//true //2.mkdir make Directory 文件夹(目录) //细节1:windows当中路径是唯一的,如果当前路径已经存在,则创建失败,返回false //细节2:mkdir方法只能创建单级文件夹,无法创建多级文件夹。 File f2 = new File("D:\\aaa\\aaa\\bbb\\ccc"); boolean b2 = f2.mkdir(); System.out.println(b2);//false //3.mkdirs 创建多级文件夹 //细节:既可以创建单级的,又可以创建多级的文件夹 File f3 = new File("D:\\aaa\\ggg"); boolean b3 = f3.mkdirs(); System.out.println(b3);//true } }

(运行结果截图)

【删除】

(源代码)

package com.itheima.a01myfile; import java.io.File; public class FileDemo5 { public static void main(String[] args) { /* public boolean delete() 删除文件、空文件夹 */ //细节: //如果删除的是文件,则直接删除,不走回收站。 //如果删除的是空文件夹,则直接删除,不走回收站 //如果删除的是有内容的文件夹,则删除失败 //1.创建File对象 File f1 = new File("D:\\aaa\\eee"); //2.删除 boolean b = f1.delete(); System.out.println(b);//true } }

(运行结果截图)

(获取并遍历)

注意:

例如:

(源代码)

package com.itheima.a01myfile; import java.io.File; public class FileDemo6 { public static void main(String[] args) { //public File[] listFiles() 获取当前该路径下所有内容 //1.创建File对象 File f = new File("D:\\aaa"); //2.listFiles方法 //作用:获取aaa文件夹里面的所有内容,把所有的内容放到数组中返回 File[] files = f.listFiles(); for (File file : files) { //file依次表示aaa文件夹里面的每一个文件或者文件夹 System.out.println(file); } } }

(运行结果截图)

(所有获取并遍历的方法)

例如:

(源代码)

package com.itheima.a01myfile; import java.io.File; public class FileDemo8 { public static void main(String[] args) { /* (掌握)public File[] listFiles() 获取当前该路径下所有内容 public File[] listFiles(FileFilter filter) 利用文件名过滤器获取当前该路径下所有内容 public File[] listFiles(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容 */ //1.创建File对象 File f = new File("D:\\aaa"); //2.需求:打印里面所有的txt文件 File[] arr = f.listFiles(); for (File file : arr) { //file依次表示aaa文件夹里面每一个文件或者文件夹的路径 if(file.isFile() && file.getName().endsWith(".txt")){ System.out.println(file); } } } }

(运行结果截图)

http://www.jsqmd.com/news/972090/

相关文章:

  • Randall-Sundrum膜世界中的紧凑物体构建与稳定性分析
  • 别再手动调格式了!用Jaspersoft Studio 6.2.0搞定PDF报表排版(附常见报错解决)
  • 电商图片下载工具技术原理:从浏览器内核到智能分类
  • 别再为没有PDB文件发愁了:用JetBrains dotPeek搭建本地符号服务器,轻松调试任意NuGet包源码
  • OriginPro 2021b 保姆级教程:三步搞定多曲线填充面积图,告别数据遮盖烦恼
  • 信号处理入门:5分钟搞懂Butterworth滤波器阶数与截止频率怎么选
  • 考研复习 Day 47 | 密码学--第七章 公钥密码(下)
  • 从Wi-Fi信号到音频均衡器:手把手拆解幅频/相频在真实电子设备中的应用
  • ESP32 ADC测量不准?深入排查Wi-Fi干扰、供电噪声与代码配置(避坑指南)
  • ESP32-S3驱动WS2812灯带:从原理图到代码,手把手搞定RMT配置
  • 别再到处找图了!我整理了全套Apriltag TAG16H5高清大图(附Python脚本一键下载)
  • ёRadio显示配置全攻略:OLED、TFT屏幕驱动与界面定制
  • 软件工程期末自救指南:避开这10个高频易错点,轻松拿下简答题和名词解释
  • TVA与MES/SCADA对接关键协议兼容方案
  • 拼多多商品图片视频批量采集:整店自动分类与高清原图
  • 别再被MicroLIB坑了!N32G45X串口打印printf的两种正确打开方式(Keil MDK实战)
  • AI 制造 AI 的奇点:深度解析“递归自我改进(RSI)”
  • 【花雕学编程】Arduino BLDC 之自主避障式辐射侦察机器人
  • 六年之约第二年年度目标
  • SpringBoot+Vue书店管理系统源码+论文
  • 避坑指南:ADS链路预算仿真时,BudNF控件报错或结果不准?可能是你没用对这个隐藏功能
  • 从FLM到烧录器:保姆级教程教你为自制的CMSIS-DAP离线下载器生成专属下载算法
  • 别再混淆了!一文讲透SAP WM里仓储单位SU、HU和Quant的区别与联系
  • 操作系统知识点
  • 多平台电商通用采集技术:一套代码打通1688/淘宝/天猫/拼多多/京东
  • 别再死磕公式了!用Python手搓一个Cartographer概率地图更新模拟器(附代码)
  • C#逆向分析工具横评:dotPeek、ILSpy、dnSpy、Reflector到底怎么选?附真实案例对比
  • 告别Electron?用Flutter 3.0从零构建你的第一个Windows桌面应用(保姆级避坑指南)
  • 别再只用tcpdump了!Linux下用tshark抓包,这5个场景效率翻倍
  • 从PCB布线到天线设计:工程师必懂的传输线理论实战避坑指南