记念我曾经的 JAVA 姿式--转

原文地址:https://segmentfault.com/a/1190000007122432?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.iojava

目前在搞 Node.js,曾经的 JAVA 知识忘了好多,为此整理了下,感叹下工业语言仍是有至关的优点的。算法

目录


Java全部的流类位于java.io包中,都分别继承字如下四种抽象流类型。

Type 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

继承自InputStream/OutputStream的流都是用于向程序中输入/输出数据,且数据的单位都是字节(byte=8bit)。

继承自Reader/Writer的流都是用于向程序中输入/输出数据,且数据的单位都是字符(2byte=16bit)。


异常

Java的异常(包括Exception和Error)分为:

  • 可查的异常(checked exceptions)

除了RuntimeException及其子类之外,其余的Exception类及其子类都属于可查异常。这种异常的特色是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,不然编译不会经过。

  • 不可查的异常(unchecked exceptions)

包括运行时异常(RuntimeException与其子类)和错误(Error)。

运行时异常和非运行时异常:

  • RuntimeException

NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等这些异常是不检查异常,程序中能够选择捕获处理,也能够不处理。这些异常通常是由程序逻辑错误引发的,程序应该从逻辑角度尽量避免这类异常的发生。运行时异常的特色是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即便没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译经过。

  • RuntimeException之外的Exception

从程序语法角度讲是必须进行处理的异常,若是不处理,程序就不能编译经过。如IOException、SQLException等以及用户自定义的Exception异常,通常状况下不自定义检查异常。

返回目录


<span id="target"></span>

注解

Java SE5内置了三种标准注解:

@Override,表示当前的方法定义将覆盖超类中的方法。 @Deprecated,使用了注解为它的元素编译器将发出警告,由于注解@Deprecated是不同意使用的代码,被弃用的代码。 @SuppressWarnings,关闭不当编译器警告信息。 

Java还提供了4中注解,专门负责新注解的建立:

  • @Target:

表示该注解能够用于什么地方,可能的ElementType参数有:
CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类、接口(包括注解类型)或enum声明

  • @Retention

表示须要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
SOURCE:注解将被编译器丢弃
CLASS:注解在class文件中可用,但会被VM丢弃
RUNTIME:VM将在运行期间保留注解,所以能够经过反射机制读取注解的信息

  • @Document

将注解包含在Javadoc中

  • @Inherited

容许子类继承父类中的注解

Example

定义注解:

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { public String id(); public String description() default "no description"; }

使用注解:

public class PasswordUtils { @UseCase(id = 47, description = "Passwords must contain at least one numeric") public boolean validatePassword(String password) { return (password.matches("\\w*\\d\\w*")); } @UseCase(id = 48) public String encryptPassword(String password) { return new StringBuilder(password).reverse().toString(); } }

解析注解:

public static void main(String[] args) { List<Integer> useCases = new ArrayList<Integer>(); Collections.addAll(useCases, 47, 48, 49, 50); trackUseCases(useCases, PasswordUtils.class); } public static void trackUseCases(List<Integer> useCases, Class<?> cl) { for (Method m : cl.getDeclaredMethods()) { UseCase uc = m.getAnnotation(UseCase.class); if (uc != null) { System.out.println("Found Use Case:" + uc.id() + " " + uc.description()); useCases.remove(new Integer(uc.id())); } } for (int i : useCases) { System.out.println("Warning: Missing use case-" + i); } } // Found Use Case:47 Passwords must contain at least one numeric // Found Use Case:48 no description // Warning: Missing use case-49 // Warning: Missing use case-50

返回目录


安全性

  1. 严格遵循面向对象的规范。这样封装了数据细节,只提供接口给用户。增长了数据级的安全性。

  2. 无指针运算。java中的操做,除了基本类型都是引用的操做。引用是不能进行增减运算,不能被直接赋予内存地址的,从而增长了内存级的安全性。

  3. 数组边界检查。这样就不会出现C/C++中的缓存溢出等安全漏洞。

  4. 强制类型转换。非同类型的对象之间不能进行转换,不然会抛出ClassCastException

  5. 语言对线程安全的支持。java从语言级支持线程。从而从语法和语言自己作了不少对线程的控制和支持。

  6. 垃圾回收。

  7. Exception。

返回目录


类加载

原理

ClassLoader使用的是双亲委托模型来搜索类的,每一个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)自己没有父类加载器,但能够用做其它ClassLoader实例的的父类加载器。

当一个ClassLoader实例须要加载某个类时,它会试图亲自搜索某个类以前,先把这个任务委托给它的父类加载器,这个过程是由上至下依次检查的,首先由最顶层的类加载器Bootstrap ClassLoader试图加载,若是没加载到,则把任务转交给Extension ClassLoader试图加载,若是也没加载到,则转交给App ClassLoader 进行加载,若是它也没有加载获得的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类。

若是它们都没有加载到这个类时,则抛出ClassNotFoundException异常。不然将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象。

JVM在断定两个class是否相同时,不只要判断两个类名是否相同,并且要判断是否由同一个类加载器实例加载的。只有二者同时知足的状况下,JVM才认为这两个class是相同的。

加载器

  1. BootStrap ClassLoader

启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等。

URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
   for (int i = 0; i < urls.length; i++) { System.out.println(urls[i].toExternalForm());  } // 也能够经过sun.boot.class.path获取 System.out.println(System.getProperty("sun.boot.class.path"))
  1. Extension ClassLoader

扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的全部jar。

  1. App ClassLoader

系统类加载器,负责加载应用程序classpath目录下的全部jar和class文件

注意:

除了Java默认提供的三个ClassLoader以外,用户还能够根据须要定义自已的ClassLoader,而这些自定义的ClassLoader都必须继承自java.lang.ClassLoader类,也包括Java提供的另外二个ClassLoader(Extension ClassLoader和App ClassLoader)在内。Bootstrap ClassLoader不继承自ClassLoader,由于它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。

返回目录


关键字

strictfp(strict float point)

strictfp 关键字可应用于类、接口或方法。使用strictfp关键字声明一个方法时,该方法中全部的float和double表达式都严格遵照FP-strict的限制,符合IEEE-754规范。当对一个类或接口使用strictfp关键字时,该类中的全部代码,包括嵌套类型中的初始设定值和代码,都将严格地进行计算。严格约束意味着全部表达式的结果都必须是 IEEE 754算法对操做数预期的结果,以单精度和双精度格式表示。

若是你想让你的浮点运算更加精确,并且不会由于不一样的硬件平台所执行的结果不一致的话,能够用关键字strictfp。

transiant

变量修饰符,若是用transient声明一个实例变量,当对象存储时,它的值不须要维持。

volatile

做为指令关键字,确保本条指令不会因编译器的优化而省略,修饰变量,保证变量每次都是从内存中从新读取。

final

  1. 修饰基础数据成员(as const)

  2. 修饰类或对象的引用

  3. 修饰方法的final(cannot overwrite)

  4. 修饰类或者参数

返回目录


初始化

父静态->子静态
父变量->父初始化区->父构造
子变量->子初始化区->子构造


多线程

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。

返回目录


线程池

concurrent下的线程池:

名称 功能
ExecutorService 真正的线程池接口
ScheduledExecutorService 能和Timer/TimerTask相似,解决那些须要任务重复执行的问题
ThreadPoolExecutor ExecutorService的默认实现
ScheduledThreadPoolExecutor 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现

Executors

  1. newSingleThreadExecutor

建立一个单线程的线程池。这个线程池只有一个线程在工做,也就是至关于单线程串行执行全部任务。若是这个惟一的线程由于异常结束,那么会有一个新的线程来替代它。此线程池保证全部任务的执行顺序按照任务的提交顺序执行。

  1. newFixedThreadPool

建立固定大小的线程池。每次提交一个任务就建立一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,若是某个线程由于执行异常而结束,那么线程池会补充一个新线程。

  1. newCachedThreadPool

建立一个可缓存的线程池。若是线程池的大小超过了处理任务所须要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增长时,此线程池又能够智能的添加新线程来处理任务。此线程池不会对线程池大小作限制,线程池大小彻底依赖于操做系统(或者说JVM)可以建立的最大线程大小。

  1. newScheduledThreadPool

建立一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

返回目录


内存模型

Java内存模型规定,对于多个线程共享的变量,存储在主内存当中,每一个线程都有本身独立的工做内存,线程只能访问本身的工做内存,不能够访问其它线程的 工做内存。工做内存中保存了主内存共享变量的副本,线程要操做这些共享变量,只能经过操做工做内存中的副原本实现,操做完毕以后再同步回到主内存当中。

如何保证多个线程操做主内存的数据完整性是一个难题,Java内存模型也规定了工做内存与主内存之间交互的协议,首先是定义了8种原子操做:

  • lock:将主内存中的变量锁定,为一个线程所独占

  • unclock:将lock加的锁定解除,此时其它的线程能够有机会访问此变量

  • read:将主内存中的变量值读到工做内存当中

  • load:将read读取的值保存到工做内存中的变量副本中。

  • use:将值传递给线程的代码执行引擎

  • assign:将执行引擎处理返回的值从新赋值给变量副本

  • store:将变量副本的值存储到主内存中。

  • write:将store存储的值写入到主内存的共享变量当中。

内存组成

堆(Heap)

运行时数据区域,全部类实例和数组的内存均今后处分配。Java虚拟机启动时建立。对象的堆内存由称为垃圾回收器 的自动内存管理系统回收。

  • News Generation(Young Generation即图中的Eden + From Space + To Space)

    • Eden 存放新生的对象

    • Survivor Space 两个 存放每次垃圾回收后存活的对象

  • Old Generation(Tenured Generation 即图中的Old Space) 主要存放应用程序中生命周期长的存活对象

非堆内存

JVM具备一个由全部线程共享的方法区。方法区属于非堆内存。它存储每一个类结构,如运行时常数池、字段和方法数据,以及方法和构造方法的代码。它是在Java虚拟机启动时建立的。除了方法区外,Java虚拟机实现可能须要用于内部处理或优化的内存,这种内存也是非堆内存。例如,JIT编译器须要内存来存储从Java虚拟机代码转换而来的本机代码,从而得到高性能。

    • Permanent Generation  (图中的Permanent Space)存放JVM本身的反射对象,好比类对象和方法对象

    • native heap

相关文章
相关标签/搜索