面试小问

263 阅读8分钟

1. 两个Integer用 == 比较是否相等

Integer a1 = 127;
Integer b1 = 127;
if(a1 == b1){
    System.out.println("相等");
}else{
    System.out.println("不相等");
}


Integer a = 128;
Integer b = 128;
if(a == b){
    System.out.println("相等");
}else{
    System.out.println("不相等");
}

输出结果 相等 不相等

结论: 就是Integer大与等于128的时候是不相等的; Jvm会自动维护8中基本类型的常量池,int常量池维护的大小是-128~127的,所以当Integer是127时,在自动装箱的过程中取得是常量池中的数据,所以两个是相等的,而超过了这个范围之后,自动装箱就会new Integer(128),这个时候就是重新创建的两个对象,就会不相等;

对于这种基本类型的对象来说==比较的是两个对象的引用地址,而不是两个对象内存的值;要想比较两个基本类型的值可以通过equals()方法比较

2. 是否可以在子线程进行startActivity()

 btn0.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Intent intent = new Intent(context, MainActivity1.class);
                        startActivity(intent);
                    }
                }).start();
            }
        });

经过测试,可以正常的启动MainActivity1。经过查阅源码后发现,Activity的startActivity方法,经过一层一层调用,并没有检测过线程,所以是可以在子线程中启动Activity的。

3. final关键字

首先,final 是一个非访问修饰符适用于变量,方法或类

  • final修饰 变量 时,被修饰的变量必须被初始化(赋值),且后续不能修改其值,实质上是常量;
  • final修饰 方法 时,被修饰的方法不能被所在类的子类重写
  • final修饰 时,被修饰的类不能被继承,并且final类中的所有成员方法,都会被隐式的指定为final方法;但成员变量则不会变

何时使用final变量:

普通变量和final变量之间的唯一区别是我们可以将值重新赋值给普通变量;但是对于final变量,一旦赋值,我们就不能改变final变量的值。因此,final变量必须仅用于我们希望在整个程序执行期间保持不变的值。

(1). final修饰引用变量

final修饰引用变量的时候,不能重新赋值,但是可以更改引用对象的内部状态。注意这里不是重新赋值;final的这个属性叫做非传递性

//比如
final StringBuffer sb = new StringBuffer("initstring");
System.out.println(sb.toString());
sb.append(" append string");
System.out.println(sb.toString());

//输出
initstring
initstring append string

这个非传递性也适合数组,因为java中数组也是对象,fianl修饰的数组也叫final数组

  • 其次,final修饰的变量可以创建时不初始化,但是必须在创建它的位置进行初始化 如下面
static final int CAPACITY;
public static void main(String argv[]){
    CAPACITY = 3;
}

//输出报错
Compiler Error: cannot assign a value to final variable CAPACITY

静态变量的final变量要么创建的时候就初始化,要么在静态块中进行初始化

public static void main(String argv[]){
    final int a;
    a = 3;
    System.out.println(a);
}
//输出结果
3

这里就是在创建的位置进行初始化,也可以直接这样写,原理是一样的。

final int a = 3;

(2). final修饰类

final与匿名内部类

匿名类(Anonymous Class)虽然说同样不能被继承,但它们并没有被编译器限制成final。另外要提到的是,网上有许多地方都说因为使用内部类,会有两个地方必须需要使用 final 修饰符:

  1. 在内部类的方法使用到方法中定义的局部变量,则该局部变量需要添加 final 修饰符
  2. 在内部类的方法形参使用到外部传过来的变量,则形参需要添加 final 修饰符

(3). fianl 修饰方法

  • 被private修饰的方法会默认被指定为final方法
  • 在java中永远不会看到同时被abstract和final一起修饰的方法;final是为了防止被子类重写,而abstrace是抽象方法为了让子类重写,所以这两个不会同时修饰同一个方法

参考这里

3. java双亲委托机制简述

  • 将某个特定的类接到类加载通知的时候,首先把加载任务委托给父类加载器,一次递归到顶层之后,如果父类能找到相应的类,则成功返回,若父类无法找到对应的类,就传递给子类。 如果A类引用了B类,则将使用A类的加载器加载B类

类加载器存在缓存,如果某个加载器以前成功加载过某个类后,再次接受到此类加载请求则直接返回,不再向上传递加载请求

  • 上下文类加载器

  • 对于类加载器而言,都保存有其父类加载器,也就是有parent字段,可以使用双亲委托,也就是自下而上的去委托给父类加载。

  • 而对于有些类,- SPI(Service Provider Interface)有些接口是 Java 核心库所提供的,而 Java 核心库是由启动类加载器【Bootstrap类加载器】来加载的,而这些接口的实现却来自于不同的 jar 包(厂商提供),Java 的启动类加载器是不会加载其他来源的 jar 包【存在于classPath路径,由应用程序类加载器:Application Classloader加载(是BootStrap的子类)】,这样传统的双亲委托模型就无法满足 SPI 的要求。而通过给当前线程设置上下文类加载器,就可以由设置的上下文类加载器来实现对于接口实现类的加载。

4.finalize()方法

//VolatileThread.class
public class VolatileThread {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("d调用了finalize");
    }
}

//main.class
public static void main(String[] args) {
    ArrayList<VolatileThread> list = new ArrayList<>();//1

    VolatileThread a = new VolatileThread();//2
    list.add(a);//3
    a = null;//4

    list.clear();//5
    System.gc();//6
    System.out.println("垃圾回收");

}

fianlize()方法是在某一个对象被gc回收的时候,该对象会调用这个方法。上面代码中,如果只赋值给a对象为null是不会调用VolatileThread对象的finalize()方法的,因为其还有list在引用,垃圾回收的时候不会回收此对象,当list.clear()之后, VolatileThread a;才真正属于无用对象,这时才会进行垃圾回收

4. synchronized 和 voliate;AtomicInteger

5. 线程池,几种线程池,和几个核心参数

线程池五个核心参数;

corePoolSize : 核心线程大小;线程池一直运行,核心线程就不会停止;

maxinumPoolSize: 最大线程池数量;非核心线程数=maxinumPoolSize - corePoolSize;

keepAliveTime:非核心线程的心跳时间;如果非核心线程在keepAliveTime的时间内没有运行任务,非核心线程就会消亡;

workQueue:阻塞队列,存放在阻塞中的线程队列;

defaultHandler:饱和策略;

ThreadFactory:线程工厂;新建线程工厂。

HashMap,HashTable,Set,List,等数据结构

Throwable和 Exception的区别和联系

6. java方法传参,

7. java内存模型,java内存分区,垃圾回收机制,几种引用(强,软,弱,虚)

红黑树

Android中什么是内存泄漏,什么是内存溢出,两者有啥区别,怎么查找内存泄漏

6. Android Binder跨进程通讯

Android性能和UI优化

Android的四种启动模式

Android签名,dex分包,handler,内存泄漏,recyclerview

Android onSavedInstanceState方法

内存不足时,由系统销毁一个Activity时,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用

onSaveInstanceState(Bundle) 方法会在 Activity 被 kill 掉之前调用。假设 Activity B 被启动,Activity A 可能会因为资源回收的原因,被 Kill 掉,那么当从 Activity B 回到 Activity A 的时候,你可以从 onCreate(Bundle) 或者 onRestoreInstanceState(Bundle) 方法获得之前保存的状态(state)。 而直接启动Activity A onCreate(Bundle)方法中的bundle是为null的。

  • 在屏幕旋转的时候也会调用onSaveInstanceState(Bundle) ,并且onCreate(Bundle)中参数部不为null

savedInstanceState 只用来存储 Activity 有关系的数据,这些数据的生命周期和 Activity 相关。这种是数据可以认为是临时数据,并不是持久数据(persistent data)。一般情况下,它比较适合存储用户的临时表单数据之类的数据。如果有需要持久存储的数据,应该在 onPause() 方法中保存数据。不能在onStop()方法中保存,因为如果

事实上,onSaveInstanceState(Bundle) 方法并不是 Activity 生命周期的方法。

Android中activity,context,application有什么不同。

参考 Activity、Service和Application的Context的区别

ContextImpl用Android studio和eclipse查看差不到,需要在Android sdk中找,路径是android.app.ContextImpl中,

java中堆内存和栈内存的简单理解

链接

java 如何判断内存回收,垃圾回收算法

链接

什么是公平锁?什么是非公平锁

简述 Java AQS 的原理以及使用场景

volatile 关键字解决了什么问题,它的实现原理是什么?

简述 BIO, NIO, AIO 的区别

简述 CAS 原理,什么是 ABA 问题,怎么解决?

HashMap 与 ConcurrentHashMap 的实现原理是怎样的?ConcurrentHashMap 是如何保证线程安全的?

Java 常见锁有哪些?ReetrantLock 是怎么实现的?

简述 Java AQS 的原理以及使用场景