Java基本知识
一、I/O
分字节流和字符流
字节流由InputStream和OutputStream读入和写入
DataInputStream继承自FilterInputStream,可以读取基本数据类型(char, byte, short, int, long, double, float, boolean)和String类型
字符流有FileReader,FileWriter,StringReader,StringWriter,CharArrayReader,CharArrayWriter,BufferedReader和BufferedWriter
InputStreamReader可以把InputStream转化为Reader,而OutputStreamReader可以把OutputStream转化为Writer。
二、如何使用DataInputStream
Socket socket;
DataInputStream in = new DataInputStream( new BufferedInputStream( socket.getInputStream() ) );
in.readByte(); in.readUTF();
三、标准I/O重定向
Java的System类提供一些简单的静态方法调用,允许我们对标准I/O和错误I/O流进行重定向:
SetIn(InputStream)
SetOut(PrintStream)
SetErr(PrintStream)
I/O重定向操作的是字节流而不是字符流,因此我们是用InputStream和OutputStream而不是Reader和Writer
四、NIO
NIO与I/O最重要的区别是数据打包和传输的方式。原来的I/O以流的方式处理数据,而NIO以块的方式处理数据。
面向流的I/O系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的I/O 通常相当慢。
面向块的I/O系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的I/O缺少一些面向流的I/O所具有的优雅性和简单性。
通道和缓冲区是NIO中的核心对象,几乎在每一个I/O操作中都要使用它们。所有数据都通过Buffer对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是InputStream或者OutputStream的子类), 而Channel可以用于读、写或者同时用于读写。因为它们是双向的,所以通道可以比流更好地反映底层操作系统的真实情况。特别是在 UNIX 模型中,底层操作系统通道是双向的。
Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。
NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。
Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容。
了解了这个基本原理,我们结合代码看看使用,在使用上,也在分两个方向,一个是线程处理,一个是用非线程,后者比较简单。
五、Java如何存储数据
- Register,这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部。但是Register的数量极其有限,所以Register由编译器根据需求进行分配。你不能直接控制.Stack,位于通用RAM中。
- Heap,用于存放所有Java对象.
- 静态存储
- 常量存储
- 非RAM存储,例如流对象和持久化对象
JVM只能回收Heap中的内容
六、Primitive Type
Primitive type |
Size |
Minimum |
Maximum |
Wrapper type |
char |
16bits |
Unicode 0 |
Unicode 215-1 |
Character |
byte |
8bits |
-128 |
127 |
Byte |
short |
16 bits |
-215 |
215-1 |
Short |
int |
32 bits |
-231 |
231-1 |
Integer |
long |
64 bits |
-263 |
263-1 |
Long |
float |
32 bits |
|
|
Float |
double |
64 bits |
|
|
Double |
boolean |
|
|
|
|
七、Java为什么可以移植
Java每种基本数据类型所占存储空间大小不随硬件架构变化而变化.
八、将float或者double值转成整型值后,总是将小数部分砍掉,而不是四舍五入。Math.random()范围为[0,1)。
九、java类加载原理和过程
java中默认有三种类加载器:启动类加载器,扩展类加载器,系统类加载器(也叫应用类加载器)。
启动类加载器bootstrap负责加载jre/lib中的系统类,这种类加载器都是用c语言实现的,在java程序中没有办法获得这个类加载器,对于java程序是一个概念而已,基本上不用考虑它的存在,像String,Integer这样的类都是由启动类加载器加载器的。
扩展类加载器ExtClassLoader负责加载jre/lib/ext中的类,一般使用java实现,这是一个真正的java类加载器,和普通的类加载器一样,其实这个类加载器对我们来说也不是很重要,我们可以通过java程序获得这个类加载器。
系统类加载器AppClassLoader加载第一个应用类的加载器(其实这个定义并不准确,下面你将会看到),也就是执行java MainClass 时加载MainClass的加载器,这个加载器使用java实现,使用的很广泛,负责加载classpath中指定的类。
类加载器之间有一定的关系(父子关系),我们可以认为扩展类加载器的父加载器是启动类加载器(当然不这样认为也是可以的,因为启动类加载器表现在java中就是一个null),不过系统类加载器的父加载器一定是扩展类加载器,类加载器在加载类的时候会先给父加载器一个机会,只有父加载器无法加载时才会自己去加载。
在ClassLoader.java(ExtClassLoader和AppClassLoader的间接父类)中的loadClass(name)首先在内存中查找该类是否被load进来了,如果没有load进来则依双亲委派原则进行查找,你的类装载器首先将请求传递给它的双亲类装载器,然后将这个请求一路委派直到委派给启动类装载器。则启动类装载器可以将java.ang.String类返回给你的类装载器,因为启动类装载器可以找到这个类,所以就不必在扩展类装载器中查找了,否则将在扩展类装载器查找,如果再找不到就在系统类装载器查找,如果系统类装载器也找不到就试图从网络下载这个类。不同类装载器中的类不能访问彼此的包内可见成员。另外说明一点,包名不能以java.开头,否则会抛出SecurityException。
十、类实例化途径
- New
- 调用Class或者java.lang.reflect.Constructor对象的newInstance()
- 调用任何对象的clone()
- 通过java.io.ObjectInputStream的getObject()反序列化
十一、ConcurrentHashMap底层实现
pasting
十二、Volatile
十三、
十四、
pastingpasting