【Java编程】JavaSE基础总结(三):异常机制、泛型

2023-11-14

JavaSE基础总结(三)

1.Java异常机制

1.1 异常

在理想的情况下,我们的程序会按照我们的思路去运行,按理说是不会出现问题的,但是,代码实际编写后并不一定是完美的,可能会有我们没有考虑到的情况,如果这些情况能够正常得到一个错误的结果还好,但是如果直接导致程序运行出现问题了呢?

当程序运行出现我们没有考虑到的情况时,就有可能出现异常或是错误!

我们在之前其实已经接触过一些异常了,比如 数组越界异常,空指针异常,算术异常 等,他们其实都是异常类型,我们的每一个异常也是一个类,他们都继承自 Exception 类!异常类型本质依然是类的对象,但是异常类型支持在程序运行出现问题时抛出,也可以提前声明,告知使用者需要处理可能会出现的异常!

异常的第一种类型是 运行时异常,在编译阶段无法感知代码是否会出现问题,只有在运行的时候才知道会不会出错(正常情况下是不会出错的),这样的异常称为运行时异常。所有的运行时异常都继承自 RuntimeException

异常的另一种类型是 编译时异常,编译时异常是明确会出现的异常,在编译阶段就需要进行处理的异常(捕获异常)如果不进行处理,将无法通过编译!默认继承自 Exception 类的异常都是编译时异常。

错误 比异常更严重,异常就是不同寻常,但不一定会导致致命的问题,而错误是致命问题,一般出现错误可能 JVM 就无法继续正常运行了,比如 OutOfMemoryError 就是内存溢出错误(内存占用已经超出限制,无法继续申请内存了)。

int[] arr = new int[Integer.MAX_VALUE];   // 能创建如此之大的数组吗?

在这里插入图片描述
错误都继承自 Error 类,一般情况下,程序中只能处理异常,错误是很难进行处理的,ErrorExecption 都继承自 Throwable 类。当程序中出现错误或异常时又没有进行处理时,程序(当前线程)将终止运行。

1.2 异常的处理

当程序没有按照我们想要的样子运行而出现异常时(默认会交给 JVM 来处理,JVM 发现任何异常都会立即终止程序运行,并在控制台打印栈追踪信息),我们希望能够自己处理出现的问题,让程序继续运行下去,就需要对异常进行捕获。

int[] arr = new int[5];
arr[5] = 1;  // 我们需要处理这种情况,保证后面的代码正常运行!
System.out.println("lbwnb");

我们可以使用 trycatch 语句块来处理。

int[] arr = new int[5];
try{    // 在 try 块中运行代码
     arr[5] = 1;    // 当代码出现异常时,异常会被捕获,并在 catch 块中得到异常类型的对象
} catch (ArrayIndexOutOfBoundsException e){   //捕获的异常类型
     System.out.println("程序运行出现异常!");  //出现异常时执行
}
//后面的代码会正常运行
System.out.println("lbwnb");

当异常被捕获后,就由我们自己进行处理(不再交给 JVM 处理),因此就不会导致程序终止运行。

我们可以通过使用 e.printStackTrace() 来打印栈追踪信息,定位我们的异常出现位置。

运行时异常在编译时可以不用捕获,但是编译时异常必须进行处理。

可以捕获到类型不止是 Exception 的子类,只要是继承自 Throwable 的类,都能被捕获,也就是说,Error 也能被捕获,但是不建议这样做,因为错误一般是虚拟机相关的问题,出现 Error 应该从问题的根源去解决。

当别人调用我们的方法时,如果传入了错误的参数导致程序无法正常运行,这时我们就需要手动抛出一个异常来终止程序继续运行下去,同时告知上一级方法执行出现了问题。

public static void main(String[] args) {
        try {
            test(1, 0);
        } catch (Exception e) {   //捕获方法中会出现的异常
            e.printStackTrace();
        }
    }

    private static int test(int a, int b) throws Exception {  // 声明抛出的异常类型
        if(b == 0) throw new Exception("0不能做除数!");      // 创建异常对象并抛出异常
        return a / b;  // 抛出异常会终止代码运行
    }

在这里插入图片描述

通过 throws 关键字抛出异常(抛出异常后,后面的代码不再执行)当程序运行到这一行时,就会终止执行,并出现一个异常。

如果方法中抛出了非运行时异常,但是不希望在此方法内处理,而是交给调用者来处理异常,就需要在方法定义后面显式声明抛出的异常类型!如果抛出的是运行时异常,则不需要在方法后面声明异常类型,调用时也无需捕获,但是出现异常时同样会导致程序终止(出现运行时异常同时未被捕获会默认交给 JVM 处理,也就是直接中止程序并在控制台打印栈追踪信息)。异常只能被捕获一次,当异常捕获出现嵌套时,只会在最内层被捕获。

如果想要调用声明编译时异常的方法,但是依然不想去处理,可以同样的在方法上声明 throws 来继续交给上一级处理。

public static void main(String[] args) throws Exception {  //出现异常就再往上抛,而不是在此方法内处理
	test(1, 0);
}

private static int test(int a, int b) throws Exception {  // 声明抛出的异常类型
	if(b == 0) throw new Exception("0不能做除数!");        // 创建异常对象并抛出异常      
	return a / b;  
}

在这里插入图片描述
main 方法都声明抛出异常时,出现异常就由 JVM 进行处理,也就是默认的处理方式(直接中止程序并在控制台打印栈追踪信息)。

异常只能被捕获一次,当异常捕获出现嵌套时,只会在最内层被捕获。

public static void main(String[] args) throws Exception {
	try{
		test(1, 0);
	} catch (Exception e){
		System.out.println("外层");
	}
}

private static int test(int a, int b){
	try{
		if(b == 0) throw new Exception("0不能做除数!");
	} catch (Exception e){
		System.out.println("内层");
		return 0;
	}
	return a/b;
}

在这里插入图片描述

1.3 自定义异常

JDK 为我们已经提前定义了一些异常了,但是可能对我们来说不够,那么就需要自定义异常。

public class MyException extends Exception {  //直接继承即可
    
}

public static void main(String[] args) throws MyException {
        throw new MyException();   //直接使用
    }

也可以使用父类的带描述的构造方法。

public class MyException extends Exception {

    public MyException(String message){
        super(message);
    }
}

public static void main(String[] args) throws MyException {
    throw new MyException("出现了自定义的错误");
}

捕获异常指定的类型,会捕获其所有子异常类型:

try {
	throw new MyException("出现了自定义的错误");
} catch (Exception e) {    // 捕获父异常类型
	System.out.println("捕获到异常");
}

当代码可能出现多种类型的异常时,我们希望能够分不同情况处理不同类型的异常,就可以使用多重异常捕获。

try {
  //....
} catch (NullPointerException e) {
            
} catch (IndexOutOfBoundsException e){

} catch (RuntimeException e){
            
}

注意,类似于 if-else if 的结构,父异常类型只能放在最后!

try {
  //....
} catch (RuntimeException e){  //父类型在前,会将子类的也捕获

} catch (NullPointerException e) {   //永远都不会被捕获

} catch (IndexOutOfBoundsException e){   //永远都不会被捕获

}

如果希望把这些异常放在一起进行处理:

try {
     //....
} catch (NullPointerException | IndexOutOfBoundsException e) {  //用|隔开每种类型即可

}

当我们希望,程序运行时,无论是否出现异常,都会在最后执行的任务,可以交给 finally 语句块来处理:

try {
    //....
}catch (Exception e){
            
}finally {
  System.out.println("lbwnb");   //无论是否出现异常,都会在最后执行
}

try 语句块至少要配合 catchfinally 中的一个。

try {
    int a = 10;
    a /= 0;
} finally {  // 不捕获异常,程序会终止,但在最后依然会执行下面的内容
    System.out.println("lbwnb"); 
}

2.泛型

2.1 初识泛型

为了统计学生成绩,要求设计一个 Score 对象,包括课程名称、课程号、课程成绩,但是成绩分为两种,一种是以优秀、良好、合格来作为结果,还有一种就是 60.0、75.5、92.5 这样的数字分数,那么现在该如何去设计这样的一个 Score 类呢?现在的问题就是,成绩可能是 String 类型,也可能是 Integer 类型,如何才能很好的去存可能出现的两种类型呢?

public class Score {
    String name;
    String id;
    Object score;  //因为Object是所有类型的父类,因此既可以存放Integer也能存放String

  	public Score(String name, String id, Object score) {
        this.name = name;
        this.id = id;
        this.score = score;
    }
}

以上的方法虽然很好地解决了多种类型存储问题,但是 Object 类型在编译阶段并不具有良好的类型判断能力,很容易出现以下的情况:

public static void main(String[] args) {

    Score score = new Score("数据结构与算法基础", "EP074512", "优秀");  //是String类型的

    //....

    Integer number = (Integer) score.score;  //获取成绩需要进行强制类型转换,虽然并不是一开始的类型,但是编译不会报错
}

运行时出现异常!

在这里插入图片描述
使用 Object 类型作为引用,取值只能进行强制类型转换,显然无法在编译期确定类型是否安全,项目中代码量非常之大,进行类型比较又会导致额外的开销和增加代码量,如果不经比较就很容易出现类型转换异常,代码的健壮性有所欠缺!(此方法虽然可行,但并不是最好的方法)。

为了解决以上问题,JDK1.5新增了泛型,它能够在编译阶段就检查类型安全,大大提升开发效率。

public class Score<T> {   //将Score转变为泛型类<T>
    String name;
    String id;
    T score;  //T为泛型,根据用户提供的类型自动变成对应类型

    public Score(String name, String id, T score) {   //提供的score类型即为T代表的类型
        this.name = name;
        this.id = id;
        this.score = score;
    }
}
public static void main(String[] args) {
    //直接确定Score的类型是字符串类型的成绩
    Score<String> score = new Score<String>("数据结构与算法基础", "EP074512", "优秀");

    Integer i = score.score;  // 编译不通过,因为成员变量score类型被定为String!
}

泛型将数据类型的确定控制在了编译阶段,在编写代码的时候就能明确泛型的类型!如果类型不符合,将无法通过编译!

泛型本质上也是一个语法糖(并不是 JVM 所支持的语法,编译后会转成编译器支持的语法,比如之前的 foreach 就是),在编译后会被擦除,变回上面的 Object 类型调用,但是类型转换由编译器帮我们完成,而不是我们自己进行转换(安全)。

// 反编译后的代码
public static void main(String[] args) {
	Score score = new Score("数据结构与算法基础", "EP074512", "优秀");
	String i = (String)score.score;   //其实依然会变为强制类型转换,但是这是由编译器帮我们完成的
    }

像这样在编译后泛型的内容消失转变为 Object 的情况称为 类型擦除(重要,需要完全理解),所以泛型只是为了方便我们在编译阶段确定类型的一种语法而已,并不是 JVM 所支持的。综上,泛型其实就是一种类型参数,用于指定类型。

2.2 泛型类

泛型类实际上就是普通的类多了一个类型参数,也就是在使用时需要指定具体的泛型类型。泛型的名称一般取单个大写字母,比如 T 代表 Type,也就是类型的英文单词首字母,当然也可以添加数字和其他的字符。

public class Score<T> {   // 将 Score 转变为泛型类<T>
    String name;
    String id;
    T score;  // T为泛型,根据用户提供的类型自动变成对应类型

    public Score(String name, String id, T score) {   //提供的 score 类型即为 T 代表的类型
        this.name = name;
        this.id = id;
        this.score = score;
    }
}

在一个普通类型中定义泛型,泛型 T 称为 参数化类型,在定义泛型类的引用时,需要明确指出类型:

 Score<String> score = new Score<String>("数据结构与算法基础", "EP074512", "优秀");

此时类中的泛型 T 已经被替换为 String 了,在我们获取此对象的泛型属性时,编译器会直接告诉我们类型:

Integer i = score.score;   // 编译不通过,因为成员变量 score 明确为 String 类型

泛型只能用于对象属性,也就是非静态的成员变量才能使用。

static T score;   //错误,不能在静态成员上定义

由此可见,泛型是只有在创建对象后编译器才能明确泛型类型,而静态类型是类所具有的属性,不足以使得编译器完成类型推断。

泛型无法使用基本类型,如果需要基本类型,只能使用基本类型的包装类进行替换!

Score<double> score = new Score<double>("数据结构与算法基础", "EP074512", 90.5);  //编译不通过

那么为什么泛型无法使用基本类型呢?回想上一节提到的类型擦除,其实就很好理解了。由于 JVM 没有泛型概念,因此泛型最后还是会被编译器编译为 Object,并采用强制类型转换的形式进行类型匹配,而我们的 基本数据类型和引用类型之间无法进行类型转换,所以只能使用基本类型的包装类来处理。

2.3 类的泛型方法

泛型方法的使用也很简单,我们只需要把它当做一个未知的类型来使用即可:

public T getScore() {    // 若方法的返回值类型为泛型,那么编译器会自动进行推断
	return score;
}

public void setScore(T score) {   // 若方法的形式参数为泛型,那么实参只能是定义时的类型
	this.score = score;
}
Score<String> score = new Score<String>("数据结构与算法基础", "EP074512", "优秀");
score.setScore(10);   // 编译不通过,因为只接受 String 类型

同样地,静态方法无法直接使用类定义的泛型(注意是无法直接使用,静态方法可以使用泛型)。

2.4 自定义泛型方法

那么如果我想在静态方法中使用泛型呢?首先我们要明确之前为什么无法使用泛型,因为之前我们的泛型定义是在类上的,只有明确具体的类型才能开始使用,也就是创建对象时完成类型确定,但是静态方法不需要依附于对象,那么只能在使用时再来确定了,所以静态方法可以使用泛型,但是需要单独定义。

public static <E> void test(E e){   // 在方法定义前声明泛型
	System.out.println(e);
}

同理,成员方法也能自行定义泛型,在实际使用时再进行类型确定:

public <E> void test(E e){
	System.out.println(e);
}

其实,无论是泛型类还是泛型方法,再使用时一定要能够进行类型推断,明确类型才行。

注意一定要区分类定义的泛型和方法前定义的泛型!

2.5 泛型引用

可以看到我们在定义一个泛型类的引用时,需要在后面指出此类型。

Score<Integer> score;  // 声明泛型为 Integer 类型

如果不希望指定类型,或是希望此引用类型可以引用任意泛型的 Score 类对象,可以使用 ? 通配符,来表示自动匹配任意的可用类型。

Score<?> score;   // score 可以引用任意的 Score 类型对象了!

那么使用通配符之后,得到的泛型成员变量会是什么类型呢?

Object o = score.getScore();   // 只能变为 Object

因为使用了通配符,编译器就无法进行类型推断,所以只能使用原始类型。

2.6 泛型的界限

现在有一个新的需求,现在没有 String 类型的成绩了,但是成绩依然可能是整数,也可能是小数,这时我们不希望用户将泛型指定为除数字类型外的其他类型,我们就需要使用到泛型的上界定义。

public class Score<T extends Number> {   //设定泛型上界,必须是 Number 的子类
    private final String name;
    private final String id;
    private T score;

    public Score(String name, String id, T score) {
        this.name = name;
        this.id = id;
        this.score = score;
    }

    public T getScore() {
        return score;
    }
}

通过 extends 关键字进行上界限定,只有指定类型或指定类型的子类才能作为类型参数。

同样的,泛型通配符也支持泛型的界限:

Score<? extends Number> score;  //限定为匹配 Number 及其子类的类型

同理,既然泛型有上限,那么也有下限:

Score<? super Integer> score;   //限定为匹配 Integer 及其父类

通过 super 关键字进行下界限定,只有指定类型或指定类型的父类才能作为类型参数。
在这里插入图片描述
在这里插入图片描述
那么限定了上界后,我们再来使用这个对象的泛型成员,会变成什么类型呢?

Score<? extends Number> score = new Score<>("数据结构与算法基础", "EP074512", 10);
Number o = score.getScore();    //得到的结果为上界类型

也就是说,一旦我们指定了上界后,编译器就将范围从原始类型 Object 提升到我们指定的上界 Number,但是依然无法明确具体类型。思考:那如果定义下限呢?

那么既然我们可以给泛型类限定上界,现在我们来看编译后结果呢:

//使用 javap -l 进行反编译
public class com.test.Score<T extends java.lang.Number> {
  public com.test.Score(java.lang.String, java.lang.String, T);
    LineNumberTable:
      line 8: 0
      line 9: 4
      line 10: 9
      line 11: 14
      line 12: 19
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      20     0  this   Lcom/test/Score;
          0      20     1  name   Ljava/lang/String;
          0      20     2    id   Ljava/lang/String;
          0      20     3 score   Ljava/lang/Number;   //可以看到 score 的类型直接被编译为 Number类

  public T getScore();
    LineNumberTable:
      line 15: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   Lcom/test/Score;
}

因此,一旦确立上限后,编译器会自动将类型提升到上限类型。

2.7 钻石运算符

我们发现,每次创建泛型对象都需要在前后都标明类型,但是实际上后面的类型声明是可以去掉的,因为我们在传入参数时或定义泛型类的引用时,就已经明确了类型,因此 JDK1.7 提供了钻石运算符来简化代码。

Score<Integer> score = new Score<Integer>("数据结构与算法基础", "EP074512", 10);  // 1.7之前

Score<Integer> score = new Score<>("数据结构与算法基础", "EP074512", 10);  // 1.7之后

2.8 泛型与多态

泛型不仅仅可以可以定义在类上,同时也能定义在接口上:

public interface ScoreInterface<T> {
    T getScore();
    void setScore(T t);
}

当实现此接口时,我们可以选择在实现类明确泛型类型或是继续使用此泛型,让具体创建的对象来确定类型。

public class Score<T> implements ScoreInterface<T>{   //将 Score 转变为泛型类 <T>
    private final String name;
    private final String id;
    private T score;

    public Score(String name, String id, T score) { 
        this.name = name;
        this.id = id;
        this.score = score;
    }

    public T getScore() {
        return score;
    }

    @Override
    public void setScore(T score) {
        this.score = score;
    }
}
public class StringScore implements ScoreInterface<String>{   // 在实现时明确类型

    @Override
    public String getScore() {
        return null;
    }

    @Override
    public void setScore(String s) {

    }
}

2.9 多态类型擦除

思考一个问题,既然继承后明确了泛型类型,那么为什么 @Override 不会出现错误呢,重写的条件是需要和父类的返回值类型、形式参数一致,而泛型默认的原始类型是 Object 类型,子类明确后变为 Number 类型,这显然不满足重写的条件,但是为什么依然能编译通过呢?

class A<T>{
    private T t;
    public T get(){
        return t;
    }
    public void set(T t){
        this.t=t;
    }
}

class B extends A<Number>{
    private Number n;

    @Override
    public Number get(){   //这并不满足重写的要求,因为只能重写父类同样返回值和参数的方法,但是这样却能够通过编译!
        return t;
    }

    @Override
    public void set(Number t){
        this.t=t;
    }
}

通过反编译进行观察,实际上是编译器帮助我们生成了两个桥接方法用于支持重写:

@Override
public Object get(){
	return this.get();//调用返回 Number 的那个方法
}

@Override
public void set(Object t ){
	this.set((Number)t ); //调用参数是 Number 的那个方法
}

3.数据结构基础

学习集合类之前,我们还有最关键的内容需要学习,同第一章一样,自底向上才是最佳的学习方向,比起直接带大家认识集合类,不如先了解一下数据结构,只有了解了数据结构基础,才能更好地学习集合类,同时,数据结构也是你以后深入学习 JDK 源码的必备条件!

顺序表、链表、栈、队列、二叉树、哈希表、二叉树、平衡二叉树、红黑树……


敬请期待后续更新!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Java编程】JavaSE基础总结(三):异常机制、泛型 的相关文章

随机推荐

  • Putty配色方案

    在使用Putty默认配色方案时 使用vim打开文件 如果遇到折叠代码 其折叠提示看不见 折叠提示与背景色同色 均是黑色 容易造成文件是空的或文件不全等误解 本文就是为了解决该问题而修改配色方案 该配色方案来源于网路资料 亲测可以使用 引用网
  • Mac终端不管输入什么指令都显示No such file or directory?

    写了一个python文件 在Mac终端输入指令总是显示 python can t open file 这是我的文件名 Errno 2 No such file or directory 看了半天 发现是环境变量的事 bash profile
  • 网络面试题:HTTPS为什么可以保证安全,怎么加密的?

    网络面试题 HTTPS为什么可以保证安全 怎么加密的 https www bilibili com video BV1w4411m7GL from search seid 3199089843343135819 一 HTTPS是什么 HTT
  • LInkedList的模拟实现

    在之前的文章笔者介绍了链表的实现 无头单向非循环链表的实现 感兴趣的各位老铁可以点进来看看 https blog csdn net weixin 64308540 article details 128397961 spm 1001 201
  • Ubuntu安装java

    转自 https www cnblogs com ziyue7575 p 13898610 html java8 apt安装 参考 https www cnblogs com zzy1024 p 11406269 html 若是没有配置国内
  • Motion Library for Unity——(Rokoko)

    unity动画插件Motion library 介绍 unity中项目设置 步骤 介绍 Motion Library是Unity编辑器的一个插件 允许你在编辑器中搜索 预览和购买市场上的动作资产 rokoko官网 unity中项目设置 需要
  • RFID系统信号通信过程

    如图
  • React 函数组件与类组件属性默认值

    一 函数组件 方式一 使用 defaultProps 设置默认值 import React from react import PropTypes from prop types function Sub props return lt g
  • 正点原子STM32 H743完成RT Thread下的LAN8720 网卡驱动 LWIP跑起来

    目前RT官网对H743的支持力度还不理想 本想按照F407的搞定网卡的套路来搞定H743的网卡 因为phy也是LAN 8720 以为会很轻松 没想到却是一条遍布荆棘的路 好在已经有不少大佬做了不少工作 终于在巨人肩膀人完成了网卡的驱动 能p
  • leetcode 1491 去掉最低工资和最高工资后的工资平均值

    leetcode 1491 去掉最低工资和最高工资后的工资平均值 题目描述 给你一个整数数组 salary 数组里每个数都是 唯一 的 其中 salary i 是第 i 个员工的工资 请你返回去掉最低工资和最高工资以后 剩下员工工资的平均值
  • 模型评估标准常用指标

    一 分类指标 样本中存在两种两种标签 样本真实标签和模型预测标签 根据这两个标签可以得到一个混淆矩阵 每一行代表样本的真实类别 数据总数表示该类别的样本总数 每一列代表样本的预测类别 数据总数表示该类别的样本总数 分类模型的评价指标主要基于
  • GPT-4 剑指多模态,前有谷歌 PaLM-E,AI 格局要变?

    本文首发自 HyperAI超神经微信公众号 美东时间 3 月 14 日 OpenAI 重磅推出大型多模态模型 GPT 4 GPT 4 是 ChatGPT 和 Bing AI 聊天机器人背后的技术基础 OpenAI 称 GPT 4 能接受图像
  • IEEE PDF eXpress系统报错:TimesNewRoman PS-BoldMT, ItalicMT, PSM

    问题 IEEE PDF eXpress系统报错 Errors Font TimesNewRomanPS BoldMT TimesNewRomanPS ItalicMT TimesNewRomanPSMT is not embedded 13
  • Python2.7和Python3.6的和平相处,pip冲突的解决办法

    第一次写 有点紧张 呈上自己遇到的一系列问题 及解决办法 我一开始在windows10下面装了python3 6 1 由于需要用到python2 7 所以昨天按照网上的教程安装 1 下载python2 7 配置环境变量 可以在下载过程中进行
  • QT ‘XXX‘ was not declared in this scope

    QT XXX was not declared in this scope 1 未定义 解决办法 变量直接使用但没有定义 定义相应的函数或变量即可 2 字符错误 解决办法 看看字母或者括号是否写错了 3 超出作用域 解决办法 增加声明 扩大
  • malloc的实现原理

    在开发c或c 时 经常需要分配内存 如今常用的分配内存函数为malloc tcmalloc jemalloc 其中属于malloc使用最平常 因为属于c标准库函数 但是网上有有实验证明另外两个效率比malloc高 这篇文章主要还是分析mal
  • openWrt 安装管理界面luci中文包

    openWrt15安装管理界面luci中文包 如果刚刷的openwrt15没有中文界面 用ssh连接路由后用opkg安装 root bang bang tang opkg install luci i18n base zh cn Unkno
  • 关于自己对像Chat-GPT的反应速度感悟

    这几个月相信大家应该对ChatGPT都不陌生了吧 因为这个东西已经在各大社交媒体可以说是无限次曝光了 就连一些其他行业的 完全跟科技行业沾不上边的朋友们 都知道了 可想而知 这个是有多火了 而我之所以发表这个感悟 其实也是自己的一个反思吧
  • matlab画矩阵无向网络图,[转]矩阵生成无向网络图

    功能是将邻接矩阵或关联矩阵变为网络图 不过这里只能转换为无向图 有向图的箭头还需要在研究一下 似乎有annotation函数可以调用 函数名netplot 使用方法输入请help netplot 无返回值 函数只能处理无向图 作者 tian
  • 【Java编程】JavaSE基础总结(三):异常机制、泛型

    JavaSE基础总结 三 1 Java异常机制 1 1 异常 在理想的情况下 我们的程序会按照我们的思路去运行 按理说是不会出现问题的 但是 代码实际编写后并不一定是完美的 可能会有我们没有考虑到的情况 如果这些情况能够正常得到一个错误的结