1. 为什么Java不支持运算符重载?
为什么 C++ 支持运算符重载而 Java 不支持? 有人可能会说+运算符在 Java 中已被重载用于字符串连接。
与 C++ 不同,Java 不支持运算符重载。Java 不能为程序员提供自由的标准算术运算符重载,例如+, - ,*和/等。如果你以前用过 C++,那么 Java 与 C++ 相比少了很多功能,例如 Java 不支持多重继承,Java中没有指针,Java中没有引用传递。
为什么 Java 不支持运算符重载?
1)简单性和清晰性。清晰性是Java设计者的目标之一。设计者不是只想复制语言,而是希望拥有一种清晰,真正面向对象的语言。添加运算符重载比没有它肯定会使设计更复杂,并且它可能导致更复杂的编译器, 或减慢 JVM,因为它需要做额外的工作来识别运算符的实际含义,并减少优化的机会, 以保证 Java 中运算符的行为。
2)避免编程错误。Java 不允许用户定义的运算符重载,因为如果允许程序员进行运算符重载,将为同一运算符赋予多种含义,这将使任何开发人员的学习曲线变得陡峭,事情变得更加混乱。据观察,当语言支持运算符重载时,编程错误会增加,从而增加了开发和交付时间。由于 Java 和 JVM 已经承担了大多数开发人员的责任,如在通过提供垃圾收集器进行内存管理时,因为这个功能增加污染代码的机会, 成为编程错误之源, 因此没有多大意义。
3)JVM复杂性。从JVM的角度来看,支持运算符重载使问题变得更加困难。通过更直观,更干净的方式使用方法重载也能实现同样的事情,因此不支持 Java 中的运算符重载是有意义的。与相对简单的 JVM 相比,复杂的 JVM 可能导致 JVM 更慢,并为保证在 Java 中运算符行为的确定性从而减少了优化代码的机会。
4)让开发工具处理更容易。这是在 Java 中不支持运算符重载的另一个好处。省略运算符重载使语言更容易处理,这反过来又更容易开发处理语言的工具,例如 IDE 或重构工具。Java 中的重构工具远胜于 C++。
2. getSupportFragmentManager()
getFragmentManager()
getChildFragmentManager()的区别
getSupportFragmentManager():Activity中使用;
getFragmentManager():Fragment中使用 获取所在Fragment的父容器的管理器;
getChildFragmentManager():Fragment中使用 获取所在Fragment里面子容器的管理器;
注:Fragment嵌套Fragment要用getChildFragmentManager().
3. Java语言如何进行异常处理?
Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。
Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。
4. 设计一个有 getMin 功能的栈
要求:
pop、push、getMin操作的时间复杂度都是O(1)
设计的栈类型可以使用现成的栈结构
/**
* 实现一个特殊的栈,在实现栈的基本功能的基础上,在实现返回栈中最小元素的操作。要求:1. pop、push、getMin操作的时间复杂度都是O(1)
* 2. 设计的栈类型可以使用现成的栈结构
*/
public class Problem01_GetMinStack {
public static class MyStack1 {
/**
* 两个栈,其中stacMin负责将最小值放在栈顶,stackData通过获取stackMin的peek()函数来获取到栈中的最小值
*/
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
/**
* 在构造函数里面初始化两个栈
*/
public MyStack1() {
stackData = new Stack<Integer>();
stackMin = new Stack<Integer>();
}
/**
* 该函数是stackData弹出栈顶数据,如果弹出的数据恰好等于stackMin的数据,那么stackMin也弹出
* @return
*/
public Integer pop() {
Integer num = (Integer) stackData.pop();
if (num == getmin()) {
return (Integer) stackMin.pop();
}
return null;
}
/**
* 该函数是先判断stackMin是否为空,如果为空,就push新的数据,如果这个数小于stackMin中的栈顶元素,那么stackMin需要push新的数,不管怎么样
* stackData都需要push新的数据
* @param value
*/
public void push(Integer value) {
if (stackMin.isEmpty()) {
stackMin.push(value);
}
else if (value < getmin()) {
stackMin.push(value);
}
stackData.push(value);
}
/**
* 该函数是当stackMin为空的话第一次也得push到stackMin的栈中,返回stackMin的栈顶元素
* @return
*/
public Integer getmin() {
if (stackMin == null) {
throw new RuntimeException("stackMin is empty");
}
return (Integer) stackMin.peek();
}
}
public static void main(String[] args) throws Exception {
/**
* 要注意要将MyStack1声明成静态的,静态内部类不持有外部类的引用
*/
MyStack1 stack1 = new MyStack1();
stack1.push(3);
System.out.println(stack1.getmin());
stack1.push(4);
System.out.println(stack1.getmin());
stack1.push(1);
System.out.println(stack1.getmin());
System.out.println(stack1.pop());
System.out.println(stack1.getmin());
System.out.println("=============");
}
}
5. 什么是原子操作?
原子操作(atomic operation)意为”不可被中断的一个或一系列操作” 。
处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作。
在Java中可以通过锁和循环CAS的方式来实现原子操作。CAS操作——Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作。
原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。
int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。
为了解决这个问题,必须保证增加操作是原子的,在JDK1.5之前我们可以使用同步技术来做到这一点。到JDK1.5,java.util.concurrent.atomic包提供了int和long类型的原子包装类,它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。
java.util.concurrent这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。
解决ABA问题的原子类:AtomicMarkableReference(通过引入一个boolean来反映中间有没有变过),AtomicStampedReference(通过引入一个int来累加来反映中间有没有变过)