前言
目的巩固基础,可以作为基础知识用,也可以尝试积累用于面试。
正文
原始数据类型
计算机有字长64位的机器字,但int 仍然是32 位(-2^31 ~2^31-1)。与此相似,double型的标准规定64位。
64 位整数,及其算数运算符(long)
16 位整数,及其算数运算符(short)
16 位字符,及其算数运算符(char)
8 位整数,及其算数运算符(byte)
32 位单精度实数,及其算数运算符(float)
修饰访问权限
类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。
对象重写了equals方法时,你必须重写hashcode方法
关系:
1、如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)
即:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
为啥重写equals:
如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。
特别指出利用equals比较八大包装对象(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,
默认比较的是值,在比较其它自定义对象时都是比较的引用地址
设么是hashcode:
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,
都是根据存储对象的hashcode值来进行判断是否相同的。
由于为了提高程序的效率才实现了hashcode方法,先进行hashcode的比较,如果不同,
那没就不必在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的,
和别人聊了聊,java规范与之对应,更好理解
规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该 相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。
规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode可能相同”。
引申:
1.hash算法(hash表的构造方法)
a.直接定制法
b.除留余数发
c.折叠法
d.平方取中法
2.解决hash冲突的方法
a.开放定址法
关键字{23,12,14,2,3,5},表长14,hash函数key%11,则表中存储如下:
地 址 0 1 2 3 4 5 6 7 8 9 10 11 12 13
关键字 23 12 14 2 3 5
b.链地址法
用数组加链表存储
泛型数组
不能像下面这样直接利用泛型变量创建数组:
T[] a = new T[];
但可以先创建一个Object类型的数组,再强制类型转化为泛型数组:
T[] a = (T[]) new Object[length];
这条语句是对的,因为对于没有限定的类型变量,类型擦除后用Object代替T,上面语句变为:
Object[] a = (Object[]) new Object[length];
调整数组的大小
Java序列化
Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程。
用到序列化的场景:
当你想把的内存中的对象保存到一个文件中或者数据库中时候(数据持久化);
利用序列化实现远程通信,即在网络上传送对象的字节序列;
- 序列化时深复制,反序列化还原后的对象地址与原来的不同。
- 序列化和反序列化可能会破坏单例。
- 序列化会忽略静态变量,即序列化不保存静态变量的状态。静态成员属于类级别的,所以不能序列化。即 序列化的是对象的状态不是类的状态。这里的不能序列化的意思,是序列化信息中不包含这个静态成员域。transient后的变量也不能序列化。
ransient使用小结 :
一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
总结 :
当父类继承Serializable接口时,所有子类都可以被序列化。
子类实现了Serializable接口,父类没有,父类中的属性不能被序列化(不报错,数据不会丢失),但是在子类中的属性仍能正确序列化
如果序列化的属性是对象,则这个对象也必须实现Serializable接口,否则会报错。
在反序列化时,如果对象的属性有修改或删减,则修改的部分属性会丢失,但不会报错。
在反序列化时,如果serialVersionUID被序列化,则反序列化时会失败
当一个对象的实例变量引用其他对象,序列化改对象时,也把引用对象进行序列化
static,transient后的变量不能被序列化
Java 集合
常用和应该常看的类
java.util
-Iterator
-ListIterator
-Iteratable
-HashMap(链表+数组)
-SortedMap
-TreeMap
-WeakHashMap(链表+引用队列)
-HashTable(链表+数组,不直接使用Legacy)
-ArrayList
-LinkedList
-AbstractSequenceList
-Vector(Legacy)
-Stack(Legacy)
-HashSet
-TreeSet
-SortedSet
-Comparable
-Comparator
-Collections
-Arrays
Set与List的区别
1、List,Set都是继承自Collection接口
2、List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的)
3、List接口有三个实现类:LinkedList,ArrayList,Vector ,Set接口有两个实现类:HashSet(底层由HashMap实现),LinkedHashSet
为什么一定要实现Iterable接口,为什么不直接实现Iterator接口呢?
看一下JDK中的集合类,比如List一族或者Set一族,都是实现了Iterable接口,但并不直接实现Iterator接口。
仔细想一下这么做是有道理的。
因为Iterator接口的核心方法next()或者hasNext() 是依赖于迭代器的当前迭代位置的。
如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。
当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。
除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。
但即时这样,Collection也只能同时存在一个当前迭代位置。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。
多个迭代器是互不干扰的。
递归
- 递归总有一个最简单的情况-方法的第一条语句总是一个包含return的条件语句
- 递归调用总是尝试解决一个规模更小的子问题,这样递归才能收敛到最简单的情况
- 递归调用的父问题和尝试解决的子问题之间不应该有交集
模块化
- 程序整体代码量很大时,每次处理的模块大小任然适中
- 可以共享和重用代码而无需重新实现
- 很容易改进的实现替换老的实现
- 可以解决编程问题建立合适的抽象模型
- 缩小调试范围
NIO 知识
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。
- java.nio 主要提供一些Buffer的类
- java.nio.channels 主要包含Channel和Selector相关的类
- java.nio.charset 主要包含字符集相关的类
- java.nio.channels.spi 主要包含提供Channel服务的类
- java.nio.charset.spi 主要包含了字符集服务的相关类
Channel (NIO 中channel是双向的既可以用来度,又可以用来写。而IO中的Stream是单向的)
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
Buffer
NIO中的关键Buffer实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。当然NIO中还有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等
Buffer的capacity,position和limit。缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
这点和一个算法很相似,回头补充回来。。。TODO LIST
Buffer的分配,分配不同的类型和存储对象
ByteBuffer buf = ByteBuffer.allocate(48);
CharBuffer buf = CharBuffer.allocate(1024);
向Buffer中写数据
写数据到Buffer有两种方式:
从Channel写到Buffer。
通过Buffer的put()方法写到Buffer里。
从Channel写到Buffer的例子
int bytesRead = inChannel.read(buf); //read into buffer.
通过put方法写Buffer的例子:
buf.put("test".getBytes());
从Buffer中读取数据
从Buffer中读取数据有两种方式:
从Buffer读取数据到Channel。
使用get()方法从Buffer中读取数据。
从Buffer读取数据到Channel的例子:
//read from buffer into channel.
int bytesWritten = inChannel.write(buf);
使用get()方法从Buffer中读取数据的例子
查看源代码打印帮助
byte aByte = buf.get();
get方法有很多版本,允许你以不同的方式从Buffer中读取数据。
例如,从指定position读取,或者从Buffer中读取数据到字节数组。
PS: Buffer 就先这样,关于深入问题和方法,文档吧,字数太多,而且时间有限。文档是个好东西
Selector
Selector运行单线程处理多个Channel,如果你的应用打开了多个通道,但每个连接的流量都很低,使用Selector就会很方便。例如在一个聊天服务器中。要使用Selector, 得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新的连接进来、数据接收等。
整理了一些资料,由于是别人文章直接上连接
一 前言
二 Channel
三 Buffer
四 Scatter/Gather
五 通道之间的数据传输 有错误但是很容易发现
六 Selector
七 FileChannel
八 SocketChannel
九 ServerSocketChannel
十 DatagramChannel
十一 Pipe
十二 Java NIO与IO 比较
虚拟机冷门知识
- 虚拟机规范严格规定了有且只有5种情况(JDK7)必须对类进行初始化(执行类构造器()方法):
1.遇到new,getstatic,putstatic,invokestatic这失调字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
3.当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
4.当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
5.当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化。
PS: 文章不断更新,因为涉及到面试,我会尽量准确描述并求证。如有错误,请留言,我会求证,修改正确。
待续。。。