字符串
String str=”i”和String str=new String(“i”)
- 内存分配不同,前者分配到常量池中,后者是在堆内存
- 创建的对象数不一样,前者只会在常量池中创建一个对象,后者可能是一个或者两个,原因在于要创建的字符串在常量池中可能没有,所以除了自身在堆中创建对象外,还需要创建常量池中的字符串对象。
为什么String是不可变的
String
类中使用final
关键字修饰字符数组来保存字符串,private final char value[]
,所以String是不可变的。
同理,由于StringBuilder
与StringBuffer
都继承自AbstractStringBuilder
类,在AbstractStringBuilder
中也是使用字符数组保存字符串char[] value
但是没有用 final 关键字修饰,所以这两种对象都是可变的。
参考:谈谈String、StringBuffer、StringBuilderObject类
Object类中哪些方法时native
getClass()
,hashCode()
,clone()
,notify()
,notifyAll()
,wait()
泛型(generics)
什么是java泛型
Java泛型是JDK5中引入的一个新特性,发型提供了编译器类型安全检查机制,该机制允许程序员在编译时期检测到非法的类型。其本质是参数化类型,也就是所操作的数据类型被指定为一个参数。假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现? 答案是可以使用 Java 泛型。 使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
类型擦除
Java的泛型是伪泛型,是因为在编译期间,所有的泛型信息都会被擦掉,即类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
使用场景
1. 泛型类
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
实例化发型类
Generic<Integer> genericInteger = new Generic<Integer>(123456);
2. 泛型接口
public interface Generator<T> {
public T method();
}
实现泛型接口,指定类型
class GeneratorImpl<T> implements Generator<String>{
@Override
public String method() {
return "hello";
}
}
3. 泛型方法
public static < E > void printArray( E[] inputArray )
{
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
使用
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray( intArray );
printArray( stringArray );
hashCode
为什么重写equals方法时,也要重写hashCode方法
- java编程规范中要求:如果两个对象调用equals方法返回true,则这两个对象的hashCode()方法也必须返回相同的哈希码。因此如果在重写equals方法时,没有重写hashCode方法,可能违法这一约定
- 使用hashCode()提前校验,可以避免每一次对比都调用equals,提高效率
包装类型的常量池技术
Java 基本类型的包装类的大部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;前面 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,Character创建了数值在[0,127]范围的缓存数据,Boolean 直接返回True Or False。如果超出对应范围仍然会去创建新的对象。 为啥把缓存设置为[-128,127]区间?性能和资源之间的权衡。且浮点数的包装类型Float,Double
没有实现常量池技术。Integer i1 = 33; Integer i2 = 33; System.out.println(i1 == i2);// 输出 true Integer i11 = 333; Integer i22 = 333; System.out.println(i11 == i22);// 输出 false Double i3 = 1.2; Double i4 = 1.2; System.out.println(i3 == i4);// 输出 false
按值传递/按引用传递?
Java总是采用按值传递。即,方法得到的是所有参数值的一个拷贝,方法内部不能修改传递给它的任务参数变量的内容。之所有会有java按引用传递的错觉,可能是因为,如果方法的入参是一个对象实例,那方法内部可以改变这个对象参数的内部属性,然而方法时不能够让该对象参数引用一个新的对象,所以这种情况下,java还是按值传递,只是传递的是该对象的内存地址。重载和重写
| 区别点 | 重载方法 | 重写方法 | | —- | —- | —- | | 发生范围 | 同一个类 | 子类 | | 参数列表 | 必须修改 | 一定不能修改 | | 返回类型 | 可修改 | 子类方法返回值类型应比父类方法返回值类型更小或相等 | | 异常 | 可修改 | 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等; | | 访问修饰符 | 可修改 | 一定不能做更严格的限制(可以降低限制) | | 发生阶段 | 编译期 | 运行期 |
接口和抽象类的区别
…
构造函数:接口不能有,抽象类可以
main方法:接口不能有,抽象类可以
访问修饰符:接口的方法默认是public,抽象类可以任意修饰符
….
成员变量和局部变量的区别
从语法角度
- 成员变量属于类的,而局部变量是在代码块或者方法中定义
- 成员变量可以被public/private/static等修饰,而局部变量不行
从内存存储方式
由于成员变量是属于类的,所以被存储在堆内存中,而局部变量是每个线程独享的,所有被分配在栈内存中从内存生存周期
成员变量是对象的一部分,所以随着对象的创建而存在;而局部变量则随着方法的调用结束而自动消亡从初始赋值角度
成员变量是属于类的,所以即使没有被显示赋初值,也会在类加载阶段被初始化赋上零值;而成员变量而不会自动赋值面向对象的三大特征
1. 继承
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。2. 封装
封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。如bean中声明的私有属性,以及提供的getter/setter方法,就是典型的封装思想。3. 多态
表示一个对象具有多种的状态。具体表现为父类的引用指向子类的实例。IO流分为几种
按流向
输入流和输出流按操作单元
字符流和字节流。字符流按8位传输,字符流按16位传输BIO、NIO、AIO区别
BIO(同步阻塞IO)
传统IO,特点是模式简单使用方便,但并发处理能力低。数据的读写必须阻塞在同一个线程内完成。NIO(同步不阻塞IO)
在JDK1.4中引入,支持面向缓冲的,基于通道的 I/O 操作方法,使用于高负债,高并发的网络应用。例如客户端和服务器端的Channel通信。AIO(异步不阻塞IO)
JDK1.7中引入,是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。