1、java中的几种数据类型,各自占用多少字节?

2、String类能被继承吗?为什么?
可以肯定的是,不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。
引申,为什么要被final修饰?
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
上面的源码看到,String类是final修饰的,并且它的属性value[]也是由final修饰的。但是value[]即使是final其数组内容也是可以变得,这里其实是private和final同时保证的。
为了实现字符串池
只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字符串。但如果字符串是可变的,那么String interning将不能实现,因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变。
字符串池:String类是我们使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存空间,这就是我们今天要讨论的核心,即字符串池(String Pool)。字符串池由String类私有的维护。
在Java中有两种创建字符串对象的方式:
1)采用字面值的方式赋值。
2)采用new关键字新建一个字符串对象。

为了线程安全
因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
为了实现String可以创建HashCode不可变性
因为字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。
另外,如果HashMap的key是可变的,结果可想而知。
3、两个对象的hashCode()相同,则equals也一定为true,对吗?
很明显是不对的,我们通过HashMap举个例子。
HashMap的结果是数组加链表(这里指jdk1.7以前,1.8加上红黑树),每次执行put时,会在数组上判断当前key的hashcode是否存在,如果存在,就存储在当前数组节点的链表上,这就是解决哈希碰撞的拉链法,当然不能直接放在链表上,还需要通过equals进行比较,存在相等的话则重复,不相等则put成功。
4、String属于基础类型吗
在问题二中明确介绍了常量池的用法,会误认为是常量,请参考问题2。
5、java中操作字符串都有哪些类,他们之间有什么区别?
String
StringBuilder
StringBuffer
1)通过继承关系来看



通过继承关系发现String和StringBuffer、StringBuilder时候没有任何相似关系的。而StringBuilder和StringBuffer继承关系相同。
2)通过创建的字符串来看



从上面的图看出,三种类型都是通过基本类型char[]存放数据,不同点在于String的长度是4,而另外两个是20,这是因为在源码中,StringBuilder和StringBuffer给的默认长度是字符串的length()加上16。
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
这两种类型其实默认大小即是16,并且支持指定大小的构造。
StringBuffer相比StirngBuilder多了一个toStringCache()。这个我猜想是应为StringBuffer是线程安全的,为了提升速度吧。
3)通过toString方法比较,这点我也有疑惑
String的toString(),直接返回当前对象。
public String toString() {
return this;
}
StringBuffer的toString()。
public synchronized String toString() {
//判断缓存的char[]是否为空
if (toStringCache == null) {
//空的话从value中范围copy过来赋值
toStringCache = Arrays.copyOfRange(value, 0, count);
}
//new一个String对象返回,传了一个boolean的share属性,但是这个属性后面并没有被使用。
return new String(toStringCache, true);
}
StringBuilder的toStirngBuilder()。
public String toString() {
// new String对象,给value、起始位置,和字符串长度
return new String(value, 0, count);
}
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
//以上所有判断都不满足,其实最终也是走这个范围复制赋值。
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
经上述比较,String返回自身,StringBuilder和StirngBuffer返回的是自身value的范围copy的字符串,同样都返回new的String对象,区别在于StringBuffer多了一个toStringCache数组,暂时没有具体的解释用在哪里。
4)通过内部的append方法
首先我们从源码看到,String的类的是final不可变的,而StringBuffer和StringBuilder是可以进行叠加的。
另外我们发现StringBuffer 的方法都是Synchrosized修饰的,即线程安全的,并且将toStringCache设置为null。
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
注意:与HashMap相同,都存在扩容的机制,我们应该尽量避免,手动指定合适的大小。
6、java中的IO流分几种
按照流的流向分,可以分为输入流和输出流;
按照操作单元划分,可以划分为字节流和字符流;
按照流的角色划分为节点流和处理流。
节点流:可以从或向一个特定的地方(节点)读写数据。
如:FileReader、FileWriter、FileInputStream、FileOutputStream等文件进行处理的节点流。
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
如:BufferedImputStrean、BufferedOutputStream、BufferedReader、BufferedWrite。
Java I0流的40多个类都是从如下4个抽象类基类中派生出来的:
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

网友评论