创建对象方式:
1.采用new 2.通过反射 3.采用clone 4.通过序列化机制
Object中有哪些公共方法?
equals()
clone()
getClass()
wait()
notify(),notifyAll()
toString()
finalize()
如果原地,不使用第三个变量交换两个变量的值
例如:int a=5,b=10;
第一种:两数的和减去b得到a的值,赋给b;两数的和再减b(此时实际是a的值),赋给a;
a=a+b; b=a-b; a=a-b;
第二种,用异或
a^=b;
b^=a;
a^=b;
java i+1<i怎么出现
当用一个char、int、long等表示了一个该类型范围内的最大数,再增加1就会越界。比如:
int a = Integer.MAX_VALUE;
System.out.println(a);
int b = a+1;
System.out.println(b);
打印出来分别是 2147483647和-2147483648,即为2的31次方-1到-2的31次方。
i - 1 > i同理,-2^31 - 1 = 2^31 - 1。
Vector和ArrayList扩容是什么倍数扩容的?
Vector会将它的容量翻倍,ArrayList只增加50%的大小
-
数据结构中堆的概念
Full Binary Tree(满二叉树): 满二叉树的所有层,包括最后一层,都是满的。
Complete Binary Tree(完全二叉树): 在完全二叉树当中,除了最后一层之外,所有层的节点都是满的,且最后一层的节点也是从左到右的。优先填满左边的节点。
Heap(堆): 堆是一种完全二叉树。在树的性质之外,堆要求节点按照大小(父节点比子节点大/父节点比子节点小)来排列。
Min Heap(最小堆): 最小的键值总是在最前面。换句话说,所有的父节点都比他们的子节点小。
Max Heap(最大堆): 最大的键值总是在最前面。换句话说,所有的父节点都比他们的子节点大。
HashSet和TreeSet有什么区别?
HashSet是由一个hash表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是O(1)。
另一方面,TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方法的时间复杂度是O(logn)。
-
float和double类型为什么会出现精度丢失的情况
首先得从计算机本身去讨论这个问题。我们知道,计算机并不能识别除了二进制数据以外的任何数据。无论我们使用何种编程语言,在何种编译环境下工作,都要先 把源程序翻译成二进制的机器码后才能被计算机识别。以上面提到的情况为例,我们源程序里的2.4是十进制的,计算机不能直接识别,要先编译成二进制。但问 题来了,2.4的二进制表示并非是精确的2.4,反而最为接近的二进制表示是2.3999999999999999。原因在于浮点数由两部分组成:指数和尾数,这点如果知道怎样进行浮点数的二进制与十进制转换,应该是不难理解的。如果在这个转换的过程中,浮点数参与了计算,那么转换的过程就会变得不可预 知,并且变得不可逆。我们有理由相信,就是在这个过程中,发生了精度的丢失。而至于为什么有些浮点计算会得到准确的结果,应该也是碰巧那个计算的二进制与 十进制之间能够准确转换。而当输出单个浮点型数据的时候,可以正确输出,如
double d = 2.4;
System.out.println(d);
这样输出的是2.4,而不是2.3999999999999999。也就是说,不进行浮点计算的时候,在十进制里浮点数能正确显示。而如果浮点数参与了计算,那么浮点数二进制与十进制间的转换过程就会变得不可预知,并且变得不可逆。
解决方案:
BigDecimal其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用 BigDecimal(double) 构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。
throw与throws的比较
1、throws出现在方法函数头;而throw出现在函数体。
2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
throw new NumberFormatException();
void function() throws NumberFormatException{}
为什么Static方法用非static变量会报错?
Static编译时加载,此时非static变量还没有被创建。
-
静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
结论:java 中静态属性和静态方法可以被继承,但是不可以被重写而是被隐藏。
静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成,不需要
继承机制即可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的
静态方法或属性称之为"隐藏"。
class Father {
static String name = "father";
static int getAge() {
return 40;
}
}
class Son extends Father {
public String getName() {
name = "son"
}
}
为什么内部类调用的外部变量必须是final修饰的?
方法中的局部变量,方法结束后这个变量就要释放掉。然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中。为了解决:局部变量的生命周期与局部内部类的对象的生命周期的不一致性问题
System.gc()和Runtime.gc() 这两个方法用来提示JVM要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于JVM的。
异常处理完成以后,Exception对象会发生什么变化?
答:Exception对象会在下一个垃圾回收过程中被回收掉。
浅拷贝和深拷贝
引用的拷贝
private static void copyReferenceObject(){
Person p = new Person(23, "zhang");
Person p1 = p;
System.out.println(p);
System.out.println(p1);
}
这里打印的结果:
com.yaolong.clone.Person@3654919e
com.yaolong.clone.Person@3654919e
可以看到,打印的结果是一样的,也就是说,二者的引用是同一个对象,并没有创建出一个新的对象。
浅拷贝
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址
深拷贝
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。
foreach与正常for循环效率对比
for循环在遍历集合时使用下标来定位集合中的元素,foreach来遍历集合时,集合必须实现Iterator接口,foreach就是使用Iterator接口来实现对集合的遍历的。foreach遍历集合是根据一个元素获取下一个元素遍历的。
所以for循环遍历ArrayList,底层是数组,地址下标是连续的,效率比foreach高。
for循环遍历LinkedList,地址下标不连续,效率比foreach低。
静态变量在什么时候加载?编译期还是运行期?静态代码块加载的时机呢?
答案:当类加载器将类加载到JVM中的时候就会创建静态变量,这跟对象是否创建无关。静态变量加载的时候就会分配内存空间。静态代码块的代码只会在类第一次初始化的时候执行一次。一个类可以有多个静态代码块,它并不是类的成员,也没有返回值,并且不能直接调用。它们通常被用初始化静态变量。
将不使用的引用对象置于null会加速回收吗?
不会。置空会变为可回收状态,JVM会智能地判断对象是否应该被回收,置于null对其没有影响。
String str=”aaa”,与String str=new String(“aaa”)一样吗?
不一样的。因为内存分配的方式不一样。
第一种,创建的”aaa”是常量,jvm都将其分配在常量池中。
第二种创建的是一个对象,jvm将其值分配在堆内存中。
String为什么要设计成final
1.线程安全
2.支持字符串常量池数据共享,节省资源,提高效率(因为如果已经存在这个常量便不会再创建,直接拿来用)
主要是为了保证安全。如果字符串是可变的,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,很可能被篡改。还有保证了线程安全。
什么叫不可改变?
就是这个值一旦在常量池被创建,是无法修改的,即便你在后面拼接一些其他字符,也会把新生成的字符串存到另外一个地址了。
String是不可变的对象, 因此在每次对String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,性能就会降低。除非用反射找到string的value[]属性值进行改变。
String/StringBuffer/StringBuilder三者区别总结:
StringBuffer和StringBuilder的一个区别是,StringBuffer在append方法前增加了一个synchronized修饰符,以起到同步的作用,为此也降低了执行效率;使用 StringBuffer和StringBuilder 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用.
基本原则:如果要操作少量的数据,用String ;单线程操作大量数据,用StringBuilder ;多线程操作大量数据,用StringBuffer。
字节流和字符流的区别?
字符流只能处理字符类型(char,纯文本可以用字符流,比如汉字,传输的时候要查询编码表,得到汉字对应的字符),
而字节流可以处理任何类型(比如图片,视频,是以二进制传输的)
字符流(一次可以处理一个缓冲区)一次操作比字节流(一次一个字节)效率高。
字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的
而字符流在操作的时候是使用到缓冲区的,需要调用flush()强制清空缓冲区才能输出。
-
接口回调的原理
总结起来,回调的核心就是回调方将本身即this传递给调用方,这样调用方就可以在调用完毕之后告诉回调方它想要知道的信息。事件流的过程就是调用方在任务完成之后,调用接口,实现该接口的回调方就会调用该接口方法,从而获取回调事件或回调信息。
参考
-
什么是双端队列 & 阻塞队列?
ArrayDeque 是基于数组实现的可动态扩容的双端队列,也就是说你可以在队列的头和尾同时插入和弹出元素。当元素数量超过数组初始化长度时,则需要扩容和迁移数据。
由于阻塞队列本身是线程安全的,队列可以安全地从一个线程向另外一个线程传递数据,所以我们的生产者/消费者直接使用线程安全的队列就可以,而不需要自己去考虑更多的线程安全问题。这也就意味着,考虑锁等线程安全问题的重任从 你 转移到了 队列 上,降低了我们开发的难度和工作量。