Java笔记:数组,异常,泛型

1、数组java

数组也是一种引用类型,其父类是Object,使用“数据类型[]”声明,如“int[] array”表示声明了一个元素类型为int类型的数组array。程序员

数组初始化语法:算法

// 静态初始化语法,即定义的时候就初始化好了全部的元素
int[] array1 = {100, 55, 30};
// 动态初始化语法,初始化时只定义好数组中元素的个数,new int[5]表示建立一个
// 有5个int类型元素的数组,可是并无初始化数组中元素的值,只是赋予了默认值,即
// 基本数据类型的默认值和引用类型的默认值null。
int[] array2 = new int[5];

 

使用数组时,应注意如下几点:数组

  • 数组是一种容器,数组当中能够存储基本数据类型的数据,也能够存储引用数据类型的数据。对于基本数据类型,数组中存储的是数据的值,而引用类型,数组当中存储的是对象的引用,即内存地址。
  • 数组由于是引用类型,因此数组对象是存储在堆内存当中的。
  • 数组一旦建立,其长度是不可变的。
  • 数组中的元素的数据类型是统一的,如int数组则表示此数组中的元素所有都是int类型的。
  • 全部数组对象都有length属性(注意不是length方法),用来获取数组中元素的个数。
  • 数组中在存储时,数组中的元素的内存地址都是连续的。
  • 数组对象的内存地址是数组中第一个元素所在的内存地址。
  • 使用下标访问数组时,若是下标超出了数组的长度,则会发生ArrayIndexOutOfBoundsException异常。

数组的访问和赋值:直接经过下标进行访问和赋值便可,如“array1[0]=22;”。工具

数组的优势和缺点:测试

  • 优势:根据下标去检索元素时效率极高,由于数组中的元素在空间地址上是连续的,而且每一个元素占用的内存空间是相同的,检索某个元素时只须要根据数组内存地址的起始位置就能够算出这个元素的内存地址,因此检索第100个元素和第100万个元素的地址的时间都是同样的。
  • 缺点:一个是,为了保证数组中每一个元素的内存地址连续性,因此在数组中间的某个位置删除或增长元素时,会涉及到元素的向前或者向后位移的操做,此时的效率就会极低。另一个是,数组不能存储大数据量,由于很难在内存空间上找到一块特别大的连续的内存空间。

数组扩容:Java中数组扩容的原理或者说方法是将小容量的数组使用“System.arraycopy”方法拷贝到大容量的数组当中,而后删除小容量的数组,Java中数组的扩容效率是较低的,因此在数组的使用当中,应该尽可能避免数组的拷贝和扩容。大数据

二维数组:二维数组,包括三位数组等多位数组,其实就是数组中的元素又是一个数组,多少维其实由这个数组的元素“套了多少层”决定的,二维就是数组中的元素是一个一维数组,同理,三位数组中的元素是一个二维数组,而后以此类推便可。spa

// 静态初始化语法
int[][] a = {
    {1, 2, 3},
    {4, 5, 6},
    {9}
};

// 动态初始化语法
int[][] array = new int[3][4];

 

Arrays工具类:这个工具类最经常使用的就是sort和binarySearch这两个方法,可是注意,二分查找方法的前提是数组已经排好序了。code

import java.util.Arrays;


public class ArrayToolsTest{
    public static void main(String[] args){
        int[] myArray = {3, 2, 6, 4};
        // sort方法能够将一个数组排序,可是注意,sort方法并无返回值,
        // 即不是返回排好序的数组,而是直接排序传入的数组
        Arrays.sort(myArray);

        // 二分查找算法的前提是须要数组已经排好序了
        // 返回值为元素在数组中的下标,元素在数组中不存在则返回-1
        // 可是这个方法多用来判断数组中有没有这个元素,由于若是数组中该元素
        // 不仅一个的话,那么返回的下标不必定是第一个元素的下标
        int indexd = Arrays.binarySearch(myArray, 6);
    }
}

 

 

2、异常对象

一、对异常的理解

异常也是类,每个异常类均可以建立异常对象。

异常继承结构:经过帮助文档中能够看到,java.lang.Object --> java.lang.Throwable,而Throwable下有两个子类Error和Exception,它们都是能够抛出的,对于这两个子类分支,Error分支下的子类(包括Error自身)称之为错误,错误一旦发生,一般是直接就退出程序了,没有办法及时去处理。而Exception分支下的子类(包括Exception自身)称之为异常,异常是能够在代码层面提早去处理的,Exception的子类又能够分为两个分支,一个分支是RuntimeException及其子类,称为运行时异常,另外一个分支就是除RuntimeException外的其它Exception的直接子类,也称为编译时异常。

编译时异常:之因此称之为编译时异常,是由于在编译阶段就能够发现并提醒程序员提早处理这种异常,对于这类异常怎么提早去处理,仍是得要实际使用中多练才能有更深的体会。

运行时异常:这类异常在编译时不会报错,可是编译经过以后在运行时又会出错,因此叫运行时异常,好比对于表达式10/0,除数为0确定是错的,可是编译器并不会识别并提醒,编译经过以后运行的时候确定就会报错了。

异常处理:处理异常的方式有两种,一种是使用throws关键字和throw关键字,将异常抛出给上一级,让上一级去处理(上一级此时必须处理这个异常);另外一种是使用“try...catch”语句把异常捕获,可是注意,捕获到了这个异常不必定要去处理它,让它直接“过”也是容许的。

二、throws抛出异常

throws使用示例:

public class ThrowsTest{
    public static void main(String[] args){
        // 这里在编译时会发生错误,也就是编译时异常,之因此有这个异常
        // 由于在func定义中有throws关键字,表示这个方法在执行过程当中可能会发生
        // 对应的异常(ClassNotFoundException),因此它的上一级必须去处理
        // 这个异常,不处理的话,编译器就会不经过。
        func();
    }
    
    // 使用throws关键字抛出可能发生的异常
    public static void func() throws ClassNotFoundException{
        System.out.println("my func!!!");
        
        // 使用throw手动抛出一个异常
        throw new ClassNotFoundException("未找到类异常!");
    }
}

 

关于throws的使用,须要注意:

  • throws抛出的异常,一般有两种处理方式,一种是继续使用throws关键字向上一级抛出一样的异常,即调用者自身不处理这个异常,让再上一级去处理。另外一种是使用try...catch语句去捕获抛出的异常。
  • Java内置类或者咱们本身定义的类若是有使用throws关键字,就表示它是编译时异常,使用的时候必需要去处理它。
  • throws抛异常时,既能够抛出具体的异常,也能够抛出它的某个父类异常,最顶级的异常类能够是Exception类,它包含了全部异常。
  • 使用throws的时机就是若是这个异常但愿调用者来处理,那么就是用throws抛出它,其余状况应该使用try捕获的方式。

三、try捕获异常

try使用示例:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            // 将可能会发生异常的语句放在try的语句块中
            // try语句块中的代码必定会去执行,直到发生异常为止
            fis = new FileInputStream("Z:\\Study\\temp.md");
            System.out.println(10 / 0);
        } catch (ArithmeticException e) {  // 捕获可能会发生的异常
            // 捕获到异常后,对异常进行处理
            // catch语句块中的代码只有捕获到异常以后才会执行
            // ArithmeticException e这个语句至关因而声明一种引用类型的变量,相似于方法的形参,e保存的是异常对象的内存地址,而且能够在catch语句块中去使用这个对象引用e。
            System.out.println("发生算术异常!!!");
        } catch (FileNotFoundException e) {  // 可使用多个catch语句块捕获不一样的异常
            // 多个catch时会从上到下依次捕获,执行了一个catch以后,其余的catch就不会执行了
            // 而且多个catch语句时,应该遵循从上到下的“从小到大”捕获,即具体异常在前,它的父类型依次日后
            System.out.println("文件不存在!!!");
        } finally {
            // finally块中的语句不管是否发生异常都会处理,哪怕try块最后有个return语句
            // 执行到return语句时也会先执行finally中的语句,再执行return
            // 能够用来处理一些不管是否发生异常都要处理的操做,如关闭文件流等
            if (fis != null){
                try{
                    fis.close();
                } catch(IOException e) {
                    e.printStackTrace();
                }
                
            }
            System.out.println("关闭文件等其余操做。。。");
        }
    }
}

 

注意,catch捕获的异常能够是具体的异常,也能够是具体异常的父类型异常,此时能够理解为多态。

异常对象中的经常使用方法:

  • getMessage():获取异常的简单描述信息。
  • printStackTrace():打印异常追踪的堆栈信息。

四、自定义异常

自定义的异常类须要继承Exception或者RuntimeException,而且须要定义无参和有参两个构造方法。

public class MyException extends Exception{
    public MyException{
        
    }
    public MyException(String s){
        super(s);
    }
}

自定义异常中的方法重写或者覆盖时须要注意一个语法问题,重写的方法抛出的异常不能比父类的方法更大或者说更宽泛,只能更小或者说更具体,好比父类异常方法抛出了IOException异常,那么异常子类中重写这个方法时就不能抛出Exception异常,可是能够抛出IOException异常或者FileNotFoundException异常。

 

3、泛型

泛型在使用尖括号“<标识符1,标识符2,...>”来表示,标识符表明的是某种类型。

泛型的做用实际上是用它定义了一个模板,定义时并无写死数据的类型,当真正使用的时候能够根据须要传入本身的数据类型。

自定义泛型:

 /*
   自定一个泛型只须要在类名以后使用<标识符>便可
   注意,此处的标识符是随意定义,就像变量名同样
  */
 public class MyGenericTest<T> {
     public static void main(String[] args) {
         // 定义的时候,传入的类型是什么,那么建立的对象使用的泛型类型就是什么类型
         MyGenericTest<String> mgt = new MyGenericTest<>();
         mgt.func("自定义泛型方法测试!");
     }
 
     /*
       若是想要使用泛型定义的类型,在方法参数中直接使用便可
      */
     public void func(T t){
         System.out.println(t);
     }
 }

 

集合中泛型的应用:

 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
 
 public class GenericTest {
     public static void main(String[] args) {
         // 指定集合中的元素类型为Pet,不能存储其它类型的元素
         // 使用new的时候能够不用再传入类型了,能够自动推断,此时的表达式<>也称为钻石表达式
         // 若是不指定泛型,也是能够的,默认就是Object类型
         List<Pet> petList = new ArrayList<>();
         Cat c = new Cat();
         Dog d = new Dog();
 
         petList.add(c);
         petList.add(d);
 
         // 迭代器的声明也须要加上泛型的定义
         Iterator<Pet> it = petList.iterator();
         while (it.hasNext()) {
             // 本来next方法返回值的类型为Object,使用泛型以后返回的类型直接就是指定
             // 的类型,不须要进行类型转换了。
             Pet p = it.next();
             p.play();
             // 固然,若是要使用具体的子类对象的方法,仍是须要转型以后才能调用
             if (p instanceof Cat){
                 Cat myCat = (Cat)p;
                 myCat.sleep();
             }
             if (p instanceof Dog){
                 Dog myDog = (Dog)p;
                 myDog.bark();
             }
         }
         /*
         输出结果:
         宠物在玩耍!
         猫咪在睡觉!
         宠物在玩耍!
         狗子在嚎叫!
         */
     }
 }
 
 
 class Pet {
     public void play() {
         System.out.println("宠物在玩耍!");
     }
 }
 
 
 class Cat extends Pet {
     public void sleep() {
         System.out.println("猫咪在睡觉!");
     }
 }
 
 
 class Dog extends Pet {
     public void bark() {
         System.out.println("狗子在嚎叫!");
     }
 }
相关文章
相关标签/搜索