Java知识点记录

      写在前面:从今天起,笔者会将在学习中碰到的一些重要的Java知识点记录下来,以便以后复习回顾。一些内容较多且比较复杂的笔者会单独写成一篇博客,对于一些零零碎碎的知识点就直接记录在本篇文章中。本文会一直持续更新。由于时间先后顺序的关系,笔者会将不同方面的知识点直接继续记录在文章末尾,这样过一段时间文章内容可能会比较乱。如果有必要,笔者会每过一段时间整理一下。

 

1. String

      String是不可变的对象,String 类为 final 型的,因此不能被继承。每当我们创建一个字符串对象时,首先就会检查字符串池中是否存在面值相等的字符串,如果有,则不再创建,直接返回字符串池中对该对象的引用,若没有则创建然后放入到字符串池中并且返回新建对象的引用。

      在使用字符串的过程中,推荐使用直接赋值(即String s=”aa”),除非有必要才会新建一个 String 对象(即String s = new String(”aa”))。

 

2. StringBuffer和StringBuilder

      StringBuffer和StringBuilder是可变的,在修改字符串时直接在原对象上修改。StringBuffer是线程安全的,因为StringBuffer的方法上都加了synchronized关键字成为同步方法,也因此一般情况下其速度一般比StringBuilder慢。

      String、StringBuffer、StringBuilder三者的使用场景:
      ①String:在字符串不经常变化的场景中可以使用 String 类,如:常量的声明、少量的变量运算等。
      ②StringBuffer:在频繁进行字符串的运算(拼接、替换、删除等),并且运行在多线程的环境中,则可以考虑使用 StringBuffer,例如 XML 解析、 HTTP 参数解析和封装等。
      ③StringBuilder:在频繁进行字符串的运算(拼接、替换、删除等),并且运行在单线程的环境中,则可以考虑使用 StringBuffer,如 SQL 语句的拼装、JSON 封装等。

      并不是所有的 String 字符串操作都会比 StringBuffer 慢,在某些特殊的情况下,String 字符串的拼接会被 JVM 解析成 StringBuilder 对象拼接,在这种情况下 String 的速度比 StringBuffer 的速度快。如:
      String name = ”I ” + ”am ” + ”chenssy ” ;
      StringBuffer name = new StringBuffer(”I ”).append(” am ”).append(” chenssy ”);

 

3. 抽象类与接口

      抽象类:
      ①抽象类是不能实例化的。
      ②抽象方法必须由子类来进行重写。
      ③只要包含一个抽象方法的抽象类,该方法必须要定义成抽象类,不管是否还包含有其他方法。
      ④抽象类中可以包含具体的方法,当然也可以不包含抽象方法。
      ⑤子类中的抽象方法不能与父类的抽象方法同名。
      ⑥abstract 不能与 final 并列修饰同一个类。
      ⑦abstract 不能与 private、static、final 或 native 并列修饰同一个方法。

      接口:
      ①1个 Interface 的所有方法访问权限自动被声明为 public,确切的说只能为 public。
      ②接口中可以定义“成员变量”,或者说是不可变的常量,因为接口中的“成员变量”会自动变为为 public static final。可以通过类命名直接访问:ImplementClass.name。
      ③接口中不存在实现的方法。
      ④实现接口的非抽象类必须要实现该接口的所有方法。抽象类可以不用实现。
      ⑤不能使用 new 操作符实例化一个接口,但可以声明一个接口变量,该变量必须引用 (refer to) 一个实现该接口的类的对象。可以使用 instanceof 检查一个对象是否实现了某个特定的接口。例如:if(anObject instanceof Comparable){}。
      ⑥在实现多接口的时候一定要避免方法名的重复。

      抽象类和接口的区别:
      ①抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
      ②抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
      ③接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
      ④一个类只能继承一个抽象类,而一个类却可以实现多个接口。
      从语法上看,抽象类可以拥有任意范围的成员数据,同时也可以拥有自己的非抽象方法,而接口只能有静态、不能修改的成员数据(public static final),方法只能定义不能实现。对子类而言,它只能继承一个抽象类(这是 java 为了数据安全而考虑的),但是却可以实现多个接口。
      从设计层次上看,抽象类是对类抽象,而接口是对行为的抽象。对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。

 

4. 内部类

      内部类可用于实现多继承。
      内部类有如下特性:
      ①内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
      ②在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
      ③创建内部类对象的时刻并不依赖于外围类对象的创建。
      ④内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。
      ⑤内部类提供了更好的封装,除了该外围类,其他类都不能访问。

 

5. 静态代码块、构造代码块、构造函数执行顺序

      静态代码块,静态,其作用级别为类,构造代码块、构造函数,构造,其作用级别为对象。
      ①静态代码块,它是随着类的加载而被执行,只要类被加载了就会执行,而且只会加载一次,主要用于给类进行初始化。
      ②构造代码块,每创建一个对象时就会执行一次,且优先于构造函数,主要用于初始化不同对象共性的初始化内容和初始化实例环境。
      ③构造函数,每创建一个对象时就会执行一次。同时构造函数是给特定对象进行初始化,而构造代码是给所有对象进行初始化,作用区域不同。
      三者的执行顺序应该为:静态代码块 > 构造代码块 > 构造函数。

public class Test {
	/**
	 * 静态代码块
	 */
	static {
		System.out.println("执行静态代码块...");
	}

	/**
	 * 构造代码块
	 */
	{
		System.out.println("执行构造代码块...");
	}

	/**
	 * 无参构造函数
	 */
	public Test() {
		System.out.println("执行无参构造函数...");
	}

	/**
	 * 有参构造函数
	 * 
	 * @param id
	 */
	public Test(String id) {
		System.out.println("执行有参构造函数...");
	}

	public static void main(String[] args) {
		System.out.println("----------------------");
		new Test();
		System.out.println("----------------------");
		new Test("1");
	}
}

 

      执行结果为:

 

执行静态代码块...
----------------------
执行构造代码块...
执行无参构造函数...
----------------------
执行构造代码块...
执行有参构造函数...

 

6. final

      final常量:①编译期常量,永远不可改变。②运行期初始化时,我们希望它不会被改变。
      final方法:所有被 final 标注的方法都是不能被继承、更改的。父类的 final 方法是不能被子类所覆盖的。
      final类:被final修饰的类都不能被继承。
      理解好final“宏变量”:

public class FinalTest {
    public static void main(String[] args){
        String s1 = "小明";
        String s2 = "小" + "明";
        System.out.println(s1 == s2);	//true        
        String str1 = "小";
        String str2 = "明";
        String s3 = str1 + str2;
        System.out.println(s1 == s3);	//false
        
        //宏替换
        final String str3 = "小";
        final String str4 = "明";
        String s4 = str3 + str4;
        System.out.println(s1 == s4);	//true    
    }
}

 

7. Java数组

 

public static void main(String[] args) {
    int[] datas = new int[]{1,2,3,4,5};
    List list = Arrays.asList(datas);
    System.out.println(list.size());
}

 

      上面程序输出1。原因如下:
      先看asList()源码:

public static <T> List<T> asList(T... a) {
    return new ArrayList<T>(a);
}

      注意这个参数:T…a,这个参数是一个泛型的变长参数,我们知道基本数据类型是不可能泛型化的,也是就说 8 个基本数据类型是不可作为泛型参数的。所以在这里数组会当做一个对象来处理,它是可以泛型的,所以上面的程序是把一个 int 型的数组作为了 T 的类型,所以在转换之后 List 中就只会存在一个类型为 int 数组的元素了。当然如果将int改为 Integer,则长度就会变成 5 了。

      asList 返回的是一个长度不可变的列表。数组是多长,转换成的列表是多长,我们无法通过 add、remove 来增加或者减少其长度。
Arrays.copyOf 拷贝是浅拷贝,包括数组的 clone() 方法、集合的 clone() 方法都是浅拷贝。
      要想实现数组等的深拷贝,可以使用序列化来实现:

public class CloneUtils {
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> T clone(T obj) {
		T cloneObj = null;
		try {
			// 写入字节流
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			ObjectOutputStream obs = new ObjectOutputStream(out);
			obs.writeObject(obj);
			obs.close();

			// 分配内存,写入原始对象,生成新对象
			ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
			ObjectInputStream ois = new ObjectInputStream(ios);
			// 返回生成的新对象
			cloneObj = (T) ois.readObject();
			ois.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return cloneObj;
	}
}


8. 匿名内部类

 

      使用匿名内部类的注意点:
      ①使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
      ②匿名内部类中是不能定义构造函数的。
      ③匿名内部类中不能存在任何的静态成员变量和静态方法。
      ④匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
      ⑤匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
 

      给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为 final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为 final。原因是拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用 final 来让该引用不可改变。

9. 泛型

      泛型的优势:①编译时更强大的类型检测;②提供自动和隐式的类型转换。

      <T>和<?>比较:①不同点:<T>用于泛型的定义,例如 class MyGeneric<T> {...}; <?>用于泛型的声明,即泛型的使用,例如 MyGeneric<?> g = new MyGeneric<>()。②相同点:都可以指定上界和下界。

      注意点:
      ①不能在静态成员中引用封闭类型参数,即不能在静态成员中使用泛型。静态方法则可以使用泛型。
      ②泛型的类型不能是基本类型。
      ③无法对类型参数使用instanceof。由于泛型参数类型在运行时都被擦除为 Object,泛型类型都被擦除为原始类,因此 obj instanceof T 和 obj instanceof ArrayList<String>, 这种使用方式都会导致编译不通过。
      ④不能直接使用new实例化类型参数。不能使用 new T() 这样来实例类型参数对象,一方面是因为泛型参数在运行时都被擦除为 Object,另外一方面,T 的具体类型在运行时才能确定,并不确定他的构造器的参数,故无法使用 new 实例化。同样 new T[5] 这种使用也不能通过编译。解决方法是采用工厂类。使用泛型类型Class,可以通过Class.newInstance()来创建泛型类型的对象。
      ⑤不能同时继承同一个泛型接口的两个变种。

interface Eat<T> {}

class Fruit implements Eat<Fruit> {}

class Apple extends Fruit implements Eat<Apple> {}  // compile error

 

      ⑥不能重载方法。
      由于擦除的原因,以下代码是不能编译通过的:

 

public void f(List<String> list) {}
public void f(List<Integer> list) {}

 

      ⑦无法使用泛型数组。
      Java 中不能创建泛型数组,如下代码编译不通过:

 

ArrayList<String>[] list = new ArrayList<String>[5]


10. Java集合框架

      List: ArrayList, LinkedList, Vector, Stack
      Set: HashSet, LinkedHashSet, TreeSet
      Queue: PriorityQueue, ArrayDeque
      Map: HashMap, LinkedHashMap, TreeMap, HashTable, WeakHashMap

      并发 
      List: CopyOnWriteArrayList
      Set: CopyOnWriteArraySet, ConCurrentSkipListSet
      Queue: ArrayBlockingQueue, LinkedBlockingQueue, DelayQueue, PriorityBlockingQueue, SynchronousQueue,             ConcurrentLinkedQueue
      Map: ConcurrentHashMap, ConcurrentSkipListMap

      Collections: synchronizedList, synchronizedMap, synchronizedSet, synchronizedSortedMap, synchronizedSortedSet,     synchronizedNavigableMap, synchronizedNavigableSet

 

11. IO

      ①字节流

      (1) InputStream、OutputStream
      InputStream抽象了应用程序读取数据的方式
      OutputStream抽象了应用程序写出数据的方式

      (2) EOF = End 读到-1就读到结尾

      (3) 输入流基本方法
      int b = in.read(); 读取一个字节无符号填充到int低八位。 -1是EOF
      in.read(byte[] buf) 读取数据填充到字节数组buf
      in.read(byte[] buf, int start, int size) 读取数据到字节数组buf从buf的start位置开始存放size长度的数据

      (4) 输出流基本方法
      out.write(int b) 写出一个byte到流,b的低八位
      out.write(byte[] buf) 将buf字节数组都写入到流
      out.write(byte[] buf, int start, int size) 字节数组buf从start位置开始写size长度的字节到流

      (5) FileInputStream --> 具体实现了在文件上读取数据

      (6) FileOutputStream实现了向文件中写出byte数据的方法

      (7) DataOutputStream/DataInputStream
      对“流”功能的扩展,可以更加方便地读取int,long,字符等类型数据
      DataOutputStream
          writeInt()/writeDouble()/writeUTF()

      (8) BufferedInputStream&BufferedOutputStream
      这两个流类为IO提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能
      从应用程序中把输入放入文件,相当于将一缸水倒入到另一个缸中
      FileOutputStream-->write()方法相当于一滴一滴地把水“转移”过去
      DataOutputStream-->writeXxx()方法会方便一些,相当于一瓢一瓢地把水“转移”过去
      BufferedOutputStream-->方法更方便,相当于一瓢一瓢先放入桶中,再从桶中倒入到另一个缸中,性能提高

 

      ②字符流

      (1) 编码问题

      (2) 认识文本和文本文件
      java的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码)
     文件是byte byte byte...的数据序列
     文本文件是文本(char)序列按照某种编码方案(utf-8,utf-16be,gbk)序列化为byte的存储结果

      (3) 字符流(Reader Writer) ---> 操作的是文本文件
      字符的处理,一次处理一个字符
      字符的底层仍然是基本的字节序列
      字符流的基本实现
       InputStreamReader完成byte流解析为char流,按照编码解析
      OutputStreamWriter提供char流到byte流,按照编码处理
      FileReader/FileWriter
      字符流的过滤器
      BufferedReader ----> readLine 一次读一行
      BufferedWriter/PrintWriter ----> 写一行
 
      ③对象的序列化,反序列化

      (1)对象序列化,就是将Object转换成byte序列,反之叫对象的反序列化

      (2)序列化流(ObjectOutputStream),是过滤流---writeObject
          反序列化流(ObjectInputStream)---readObject

      (3)序列化接口(Serializable)
      对象必须实现序列化接口,才能进行序列化,否则将出现异常
      这个接口,没有任何方法,只是一个标准

      (4)transient关键字
      private void writeObject(ObjectOutputStream s) 
            throws IOException
      private void readObject(ObjectInputStream s) 
            throws IOException, ClassNotFoundException

 

12.序列化与反序列化

      使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。必须注意地是,对象序列化保存的是对象的”状态”,即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。

      知识点:
      ①在Java中,只要一个类实现了java.io.Serializable接口,那么它就可以被序列化。
      ②通过ObjectOutputStream和ObjectInputStream对对象进行序列化及反序列化。
      ③虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID)。
      ④序列化并不保存静态变量。
      ⑤要想将父类对象也序列化,就需要让父类也实现Serializable 接口。
      ⑥Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
      ⑦服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。

      ArrayList的序列化:
      ①为什么elementData是transient的:ArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长长度设为100,而实际只放了一个元素,那就会序列化99个null元素。为了保证在序列化的时候不会将这么多null同时进行序列化,ArrayList把元素数组设置为transient。
      ②重写writeObject 和 readObject方法的方式把元素保留下来:writeObject方法把elementData数组中的元素遍历的保存到输出流(ObjectOutputStream)中。readObject方法从输入流(ObjectInputStream)中读出对象并保存赋值到elementData数组中。

 

13.访问控制修饰符

      Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
      ①default (即缺省,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
      ②private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
      ③public : 对所有类可见。使用对象:类、接口、变量、方法
      ④protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
      我们可以通过以下表来说明访问权限:

 

修饰符当前类同一包内子孙类其他包其他包子孙类
privateYNNNN
defaultYYNNN
protectedYYYNY/N(见下面说明)
publicYYYYY

      protected 需要从以下两个点来分析说明:
      ①子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
      ②子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。

 

14.JSP

      生命周期:
      ①编译阶段:解析JSP文件,将JSP文件转为servlet,servlet容器编译servlet源文件,生成servlet类。
      ②初始化阶段:加载与JSP对应的servlet类,创建其实例,并调用它的初始化方法。
      ③执行阶段:调用与JSP对应的servlet实例的服务方法。
      ④销毁阶段:调用与JSP对应的servlet实例的销毁方法,然后销毁servlet实例。

      JSP指令:
      ①<%@ page ... %>:定义网页依赖属性,比如脚本语言、error页面、缓存需求等等。
      ②<%@ include ... %>:包含其他文件。
      ③<%@ taglib ... %>:引入标签库的定义。

      JSP动作元素:
      ①jsp:include  在页面被请求的时候引入一个文件。动态include。
      ②jsp:useBean  寻找或者实例化一个JavaBean。
      ③jsp:setProperty  设置JavaBean的属性。
      ④jsp:getProperty  输出某个JavaBean的属性。
      ⑤jsp:forward  把请求转到一个新的页面。
      ⑥jsp:plugin  根据浏览器类型为Java插件生成OBJECT或EMBED标记。
      ⑦jsp:element  定义动态XML元素
      ⑧jsp:attribute  设置动态定义的XML元素属性。
      ⑨jsp:body  设置动态定义的XML元素内容。
      ⑩jsp:text  在JSP页面和文档中使用写入文本的模板。

      JSP隐式对象:
      ①request:HttpServletRequest类的实例
      ②response:HttpServletResponse类的实例
      ③out:JspWriter类的实例,用于把结果输出至网页上
      ④session:HttpSession类的实例
      ⑤application:ServletContext类的实例,与应用上下文有关
      ⑥config:ServletConfig类的实例
      ⑦pageContext:PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问
      ⑧page:类似于Java类中的this关键字
      ⑨Exception:Exception类的对象,代表发生错误的JSP页面中对应的异常对象

 

15.Servlet

      Servlet生命周期:
      ①Servlet 通过调用 init () 方法进行初始化。
      ②Servlet 调用 service() 方法来处理客户端的请求。
      ③Servlet 通过调用 destroy() 方法终止(结束)。
      ④最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

 

16.JDBC

      编程步骤:

      ①加载驱动程序,利用Java的反射机制:

Class.forName("com.mysql.jdbc.Driver")

 

      ②通过DriverManager获取数据库连接,即调用静态工厂方法创建Connection对象:

 

DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "root");

 

      ③通过Connection对象创建Statement对象:

 

conn.createStatement();
//conn.prepareStatement(sql);

      ④执行SQL语句

      ⑤操作SQL语句返回的结果集

      ⑥关闭数据库资源

17.线程池

      线程池处理异常:①try-catch捕获异常;②submit执行,Future.get()接受异常;③重写ThreadPoolExecutor.afterExecute()方法,处理传递的异常引用;④实例化时,传入自己的ThreadFactory,设置Thread.UncaughtException。

18.Happen-Before原则

指令不能重排:Happen-Before原则:
      ①程序顺序原则:一个线程内保证语义的串行性
      ②volatile规则:volatile变量的写,先发生于读,这保证了volatile变量的可见性
      ③锁规则:解锁(unlock)必然发生在随后的加锁(lock)前
      ④传递性:A先于B,B先于C,那么A必然先于C
      ⑤线程的start()方法先于它的每一个动作
      ⑥线程的所有操作先于线程的终结(Thead.join())
      ⑦线程的中断(interrupt())先于被中断线程的代码
      ⑧对象的构造函数执行、结束先于finalize()方法

19.Java整型类型缓存

      在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用,此规则适用于整数区间 -128 到 +127。这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存。
      这种缓存行为不仅适用于Integer对象。针对所有整数类型的类都有类似的缓存机制:
      有 ByteCache 用于缓存 Byte 对象
      有 ShortCache 用于缓存 Short 对象
      有 LongCache 用于缓存 Long 对象
      有 CharacterCache 用于缓存 Character 对象
      Byte,Short,Long 有固定范围: -128 到 127。对于 Character, 范围是 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行。

20.final

      final关键字可以用于成员变量、本地变量、方法以及类。
      final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。
      你不能够对final变量再次赋值。
      本地变量必须在声明时赋值。
      在匿名类中所有变量都必须是final变量。
      final方法不能被重写。
      final类不能被继承。
      final关键字不同于finally关键字,后者用于异常处理。
      final关键字容易与finalize()方法搞混,后者是在Object类中定义的方法,是在垃圾回收之前被JVM调用的方法。
      接口中声明的所有变量本身是final的。
      final和abstract这两个关键字是反相关的,final类就不可能是abstract的。
      final方法在编译阶段绑定,称为静态绑定(static binding)。
      没有在声明时初始化final变量的称为空白final变量(blank final variable),它们必须在构造器中初始化,或者调用this()初始化。不这么做的话,编译器会报错“final变量(变量名)需要进行初始化”。
      将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。
      按照Java代码惯例,final变量就是常量,而且通常常量名要大写。
 

21.异常

try-with-resources:大多数情况下,我们使用finally块来关闭资源,有时我们忘记关闭它们并在资源耗尽时获得运行时异常。这些异常很难调试,我们可能需要查看我们使用该类资源的每个地方,以确保我们关闭它。使用Java 7的try-with-resources,我们可以在try语句中创建一个资源并在try-catch块中使用它。当执行来自try-catch块时,运行时环境会自动关闭这些资源。

Java中的final,finally和finalize的区别:
      ①final和finally是java中的关键字,而finalize是一种方法。
      ②final关键字可以与类变量一起使用,以便它们不能被重新分配,类可以避免按类扩展,并且使用方法来避免子类覆盖。
      ③finally关键字与try-catch块一起使用,以提供始终执行的语句即使出现一些异常,通常最终也会用来关闭资源。
      ④finalize()方法由垃圾收集器在销毁对象之前执行,这是确保关闭所有全局资源的好方法。
 

(未完待续)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值