JAVA基础复习

Review of Java

Posted by Jayn on February 23, 2016

要开始好好准备找实习,找工作了!

1.java中有没有指针?

  • java中没有指针,只有引用,也可以说是阉割版的指针,因为C/C++中的指针式可以在内存上任意移动的,不限制内存中数据类型,java中的reference,如Person P=New Person() 这个reference P就是只能指向类型为Person的内存
  • C语言的指针可以指向任何地方,只要你愿意,java的引用不行。
  • C语言的指针可以参与数值运算,加法、减法,java的引用不行。

2.java自动装箱

就是将基本数据类型(java中有8个int,short,long,boolean,char,float,byte,double)自动转换为Integer,Boolean等封装类型,相应的反转换就是自动拆箱 关于自动装箱的问题刚刚看到一个之前不太了解的问题:

public class Test03 {

    public static void main(String[] args) {
        Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;

        System.out.println(f1 == f2);
        System.out.println(f3 == f4);
    }
}

如果不明就里很容易认为两个输出要么都是true要么都是false。首先需要注意的是f1、f2、f3、f4四个变量都是Integer对象引用,所以下面的==运算比较的不是值而是引用。

装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

也就是说当赋值范围在IntegerCache.lowIntegerCache.high时,不会new出新的对象,若赋值范围不再这个之间,则new出新的对象,所以上面的输出为truefalse

3.final与static

  • final可以用于修饰非抽象类,非抽象方法
  • 用final修饰的成员变量表示常量,一旦给定值,就不能再改变,只能赋值一次
  • final变量一般在声明时初始化,这样就是在编译器初始化
  • final变量也可以在声明时不初始化,而在构造函数中进行初始化,这样就不是在编译器初始化
  • 当参数声明为final时,可以使用该参数,但是不能修改参数(跟C++中的const关键字相同)
  • final修饰类时,final类不能被继承,final类的成员方法不能被覆盖,默认都是final的 final方法不能被子类覆盖
  • 使用final方法原因:1.方法锁定,防止集成类修改他的方法和意义;2。高效,编译器遇到final方法会使用内嵌机制(有inline的感觉)

4.static方法可以覆盖吗?

当然不可以,因为覆盖是运行时动态绑定的,而static方法是编译时静态绑定的,static方法和任何的实例无关;使用static修饰变量和方法,类的所有实例都共享static修饰的方法和变量,不会有副本;final修饰的变量,每个实例会在创建时创建一个副本到常量池中

5.java接口VS抽象类

  1. 接口中的方法都是抽象的,没有实现;抽象类中的方法可以有实现,也可以有抽象方法
  2. 类实现接口的话必须要实现接口的所有方法,但是继承抽象类则不用实现其中的所有方法
  3. 类可以实现多个接口,但只能继承一个抽象类
  4. 接口中的成员变量是public static final类型的,接口中不能有变量,必须是常量的,且是只读不能修改的(interface的思想就是提供一个统一的公共的协议);抽象类中可以有非final变量
  5. 接口中的成员函数是public,不允许有private;但是抽象类中可以有private,proteced
  6. 接口和抽象类都不可以被实例化

6.值传递VS引用传递?

  • 对象被值传递,意味着传递了对象的一个副本。因此,就算是改变了对象副本,也不会影响源对象的值。
  • 对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象所做的改变会反映到所有的对象上。
  • java的参数传递方式只有一种:值传递,在java中对象作为参数传递时,是把对象在内存中的地址拷贝了一份传给了参数,也就是说传递的是对象引用的副本,而不是引用本身,所以这是值传递。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。具体的原理可以参考这里。这一点与C/C++不同,C/C++有两种参数传递方式,当参数传递使用引用传递时,在方法内部修改地址所指向的对象,会影响到原来的对象的值。

7.java多线程的实现方式

  1. 实现Runable接口,实现run方法,将实现Runable的类放入Thread中,start
  2. 继承Thread类,实现run方法,start启动
  3. 使用executor框架实现线程的管理:executorService中的execute方法执行任务,任务就是实现Runable或Callable接口的类 Runable接口没有返回值

Callable中的call()方法类似Runnable的run()方法,就是前者有返回值,后者没有。 当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。 同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null.

8.Java内存模型

  • 所有的变量都存储在主内存中(JVM内存的一部分)
  • 每一个线程都有自己的工作内存,线程的工作内存中存储的是主内存中相应变量的拷贝,线程不能直接对主内存中的变量进行读写操作
  • 每一个线程的工作空间是独立的,不同的线程不能互相访问彼此的工作内存。线程间变量的传递通过主内存来进行

9.内存间的交互操作

  • Lock(锁定):作用于主内存中的变量,把一个变量标识为一条线程独占的状态。 Read(读取):作用于主内存中的变量,把一个变量的值从主内存传输到线程的工作内存中。
  • Load(加载):作用于工作内存中的变量,把read操作从主内存中得到的变量的值放入工作内存的变量副本中。
  • Use(使用):作用于工作内存中的变量,把工作内存中一个变量的值传递给执行引擎。
  • Assign(赋值):作用于工作内存中的变量,把一个从执行引擎接收到的值赋值给工作内存中的变量。
  • Store(存储):作用于工作内存中的变量,把工作内存中的一个变量的值传送到主内存中。
  • Write(写入):作用于主内存中的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
  • Unlock(解锁):作用于主内存中的变量,把一个处于锁定状态的变量释放出来,之后可被其它线程锁定。

10.线程的几种状态

  • 就绪(Runnable):线程准备运行,不一定立马就能开始执行。
  • 运行中(Running):进程正在执行线程的代码。
  • 等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
  • 睡眠中(Sleeping):线程被强制睡眠。
  • I/O阻塞(Blocked on I/O):等待I/O操作完成。
  • 同步阻塞(Blocked on Synchronization):等待获取锁。
  • 死亡(Dead):线程完成了执行。

11.什么是线程安全?

如果你的代码会有多个线程执行,而每次多线程执行的结果都和单线程执行的结果一致,则说明是线程安全的。 Java集合可以分为线程安全和非线程安全两大类。

下面是这些线程安全的同步的类:

  • vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
  • statck:堆栈类,先进后出
  • hashtable:就比hashmap多了个线程安全
  • enumeration:枚举,相当于迭代器
  • 除了这些之外,其他的都是非线程安全的类和接口。 线程安全的类其方法是同步的,每次只能一个访问。是重量级对象,效率较低

12.同步方法和同步代码块的区别是什么?

  • Synchronized关键字可以修饰成员方法和代码块,被该关键字修饰的代码,只能被一个线程执行
  • 线程通过Synchronized关键字可以获得对象的锁(每个对象都有一个锁),此时禁止其他线程访问