分类 面试 下的文章

JAVA内存区域

  • JDK1.8之前 分为线程共享的:堆和方法区 线程私有的栈和计数器 以及直接内存
  • JDK1.8之后 分为线程共享的堆 原来的方法区被移动到了直接内存里的元空间,线程私有的还是栈和计数器。

计数器的作用

  • 字节码解释器通过改变计数器来依次读取指令,从而实现代码的流程控制。
  • 多线程情况下程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够继续执行。

栈的主要构成

  • 局部变量表
  • 方法出口信息
  • 操作数栈
  • 动态链接

方法/函数如何调用

  • 调用函数会压入对应的栈帧,每一个函数调用结束后,会有一个栈帧被弹出。
  • JAVA有两种返回方式

    • return 语句
    • 抛出异常

堆是JAVA虚拟机管理内存中最大的一块,Java堆是所有线程共享的一块内存区域,在虚拟机启动时创建,此区域存放了所有的对象实例, 也是垃圾收集器管理的主要区域。因此你也被称为GC堆,
JAVA堆分为

  • 老年代和新生代(Eden空间、From Survivor ToSurvivor、Tentired)空间 进一步划分的目的是更好地回收内存或者更快的分配内存。新生代在经过一定次数的GC后如果还存活就会晋升到老年代。

方法区和永久代

方法区是虚拟机规范定义的一个规范,永久代是这个规范的实现。
已被虚拟机加载的类信息、常亮、静态变量、即时编译器编译后的代码数据等

为什么要将永久代替换为元空间呢

因为原来的永久代是存放于堆空间的 堆空间是有内存限制的,元空间放在了堆外空间,不受堆内存大小限制。

运行时常量池

运行时常量池是方法区的一部分,JDK1.7以后的版本将常量池从方法区中移了出来,

堆外内存

JDK1.4以后新增的IO/NIO类引入了一种基于通道与缓存区的IO方式,可以直接使用Native函数库直接分配堆外内存,可以在一些场景显著提高性能,避免了Java堆和Native堆之间来回复制数据。

Java对象的创建过程

  • 类加载检查
  • 分配内存 (标记-清除, 标记-整理)
  • 内存分配并发问题(CAS+失败重试,TLAB(为每一个线程预先在Eden区分配一块内存,JVM给线程中的对象分配内存时首先在TLAB上分配))
  • 初始化零值
  • 初始化对象头
  • 执行init方法
    -

什么是进程和线程

  • 进程是程序的一次执行过程,系统运行程序的基本单位
  • 同一个进程的多个线程共享进程的堆和方法区资源,但是线程有自己独立的程序计数器,虚拟机栈,本地方法栈等。
    总结:线程是进程划分成的更小的运行单位,线程和进程最大的不同在于基本上各进程是独立的,而各线程不一定。线程执行开销极小,但不利于资源的管理行业保护,进程相反。

程序计数器为什么是私有的

  • 在多线程的情况下线程需要记录当前所执行到的代码位置,当线程被切出去又切回来之后可以从上次执行的代码位置继续执行。因为是多线程所以每个线程执行的位置是不一样的。

虚拟机栈和本地方法栈为什么是私有的

  • 虚拟机栈主要存放局部变量,操作数栈,常量引用池等信息,因为这些信息对外部不可见。

本地方法栈

  • 和虚拟机栈作用类似

堆和方法区

堆和方法区是所有线程的共享资源,其中堆是进程中最大的一块内存,主要用于存放新创建的对象(所有对象都在这里分配内存),方法区主要存放已被加载的类信息,常量,静态变量,即时编译器后的代码等数据。

并发与并行

  • 并发: 同一时间段,多个任务都在执行
  • 并行: 单位时间内,多个任务同时执行。

多线程的优势

  • 相比进程来说,线程的上下文开销小
  • 能够共享进程资源

多线程可能带来的问题

  • 内存泄漏
  • 上下文切换
  • 死锁

线程的生命周期和状态 6种

  • NEW 初始状态 还没调用START
  • RUNABLE 运行状态
  • BLOCKED 阻塞状态
  • WAITING 等待状态(通知和中断)
  • TIME_WAIT 超时等待(在指定时间自行返回)
  • TERMINATED 终止

线程运行过程

先调用start,等待cpu分配时间片,系统准备就绪,然后建立一个新线程调用run方法

什么是上下文切换

每个代码执行都会对堆栈,寄存器等相关信息有所影响,那么当CPU调度到别的进程的时候要把当前的现场(堆栈,寄存器等)相关信息存储起来以备下次调度回来的时候还原这个过程叫上下文切换。

线程死锁

多个线程互相等待对方释放资源
产生死锁的必须具备以下四个条件:

1. 互斥条件 该资源任意时刻只能被一个线程占用
2. 请求与保持条件 一个线程因请求资源而阻塞时,对已获得的资源不释放
3. 不剥夺条件 线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
4. 循环等待条件 若干线程之间形成一种头尾相接的循环等待资源关系

想要避免死锁只需要破坏上述四个条件的其中一个条件即可。具体如下

1. 破坏请求与保持条件 一次性申请所有的资源
2. 破坏不剥夺条件 占用部分资源的线程进一步申请其他资源时,如果申请不到 可以主动释放它占有的资源
3. 破坏循环等待条件 按序申请资源预防,按某一顺序申请资源,释放资源则按反序释放

sleep和wait方法区别和共同点

  • 共同点 都可以让线程进行登台
  • 不同点 sleep方法没有释放锁,而wait释放了锁,wait用于线程间的交互/通信 sleep通常用于暂停执行 wait方法调用后不会自动苏醒,需要别的线程调用同一个对象上的notify或者notifyAll(), sleep会自动苏醒

能否直接调用线程的run

直接调用run相当于是在本线程执行代码并不是创建线程执行。

synchronized 关键字的了解

synchronized 解决了多个线程之间的访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

在早期java中synchronized属于重量级锁,效率低下,具体是因为监视器锁(monitor)依赖于底层的操作系统的Mutex Lock来实现,Java的线程是映射到操作系统的原生线程上,如果挂起或唤醒一个线程都需要操作系统帮忙完成。而操作系统实现线程之间的切换需要从用户态转到内核态,这个转换时间成本相对较高。 在java6之后synchronized得到了较大的优化 主要把他细分成了以下锁级别:

锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级,关于重量级锁,前面我们已详细分析过,下面我们将介绍偏向锁和轻量级锁以及JVM的其他优化手段。

  • 偏向锁
    偏向锁是Java 6之后加入的新锁,它是一种针对加锁操作的优化手段,经过研究发现,在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁(会涉及到一些CAS操作,耗时)的代价而引入偏向锁。偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。所以,对于没有锁竞争的场合,偏向锁有很好的优化效果,毕竟极有可能连续多次是同一个线程申请相同的锁。但是对于锁竞争比较激烈的场合,偏向锁就失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这种场合下不应该使用偏向锁,否则会得不偿失,需要注意的是,偏向锁失败后,并不会立即膨胀为重量级锁,而是先升级为轻量级锁。
  • 轻量级锁
    倘若偏向锁失败,虚拟机并不会立即升级为重量级锁,它还会尝试使用一种称为轻量级锁的优化手段(1.6之后加入的),此时Mark Word 的结构也变为轻量级锁的结构。轻量级锁能够提升程序性能的依据是“对绝大部分的锁,在整个同步周期内都不存在竞争”,注意这是经验数据。需要了解的是,轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场合,就会导致轻量级锁膨胀为重量级锁。
  • 自旋锁
    轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。这是基于在大多数情况下,线程持有锁的时间都不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,毕竟操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,因此自旋锁会假设在不久将来,当前的线程可以获得锁,因此虚拟机会让当前想要获取锁的线程做几个空循环(这也是称为自旋的原因),一般不会太久,可能是50个循环或100循环,在经过若干次循环后,如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化方式,这种方式确实也是可以提升效率的。最后没办法也就只能升级为重量级锁了。
  • 锁消除
    消除锁是虚拟机另外一种锁的优化,这种优化更彻底,Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间,如下StringBuffer的append是一个同步方法,但是在add方法中的StringBuffer属于一个局部变量,并且不会被其他线程所使用,因此StringBuffer不可能存在共享资源竞争的情景,JVM会自动将其锁消除。

双重检验锁实现对象单例

public class Singleton {
    private volatile static Singleton uniqueInstance;
    // 使用volatile 可以防止指令重排。
    private Singleton() {
    
    }
    
    public static Singleton getUniqueInstance(){
        if(uniqueInstance == null){
            synchronized(Singleton.class){
                if(uniqueInstance == null){
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

synchronized 的底层原理

  1. 当synchronized作用于语句块时 会自动转换为monitorenter和monitorexit指令,当执行mointorenter指令时会对锁对象的计数器+1当执行monitorexit时减1 当线程调用mointorenter时会检测计数器是否为0 不为0则等待。
  2. 当synchronized作用于方法时 会给方法标志一个ACC_SYNCHRONIZED访问标志,JVM通过该ACC_SYNCHRONIZED访问标志来判定是否为同步方法而执行相应的同步调用。

synchronized和ReentrantLock

  1. 两者都是可重入锁 线程可自己再次获取已获取到的锁而无需等待。
  2. synchronized依赖于JVM而ReentrantLock依赖于API
  3. ReentrantLock比synchronized增加了一些高级功能

    1. 等待可中断
    2. 可实现公平锁
    3. 可实现选择性通知(线程对象可以注册在指定的condition中,从而可以有选择性的进行线程通知。)
    4. ReentrantLock提供了一种能够中断等待锁的线程的机制
    5. ReentrantLock可以指定是公平锁才是非公平锁(通过构造函数 new ReentrantLock(boolean fair)),synchronized只能是非公平锁。

volatile关键字原理

JDK1.2之前 Java内存模型都是从主存读取变量 而在JDK1.2以后变成了可以把变量保存在本地上(如寄存器上)这就可能造成另一个线程在主存中修改了一个变量的值,而另一个线程还继续使用它在寄存器中的变量值得拷贝,造成了数据不一致。把变量声明为volatile之后,每次读取变量都会从主存处读取,还可以防止指令重排序。

synchronized关键字和volatile关键字的区别

  • volatile关键字是线程同步的轻量级实现,volatile性能比synchronized好
  • volatile只能修饰变量 而synchronized可以修饰方法以及代码块
  • 多线程访问volatile关键字不会发阻塞,而synchronized可能会
  • volatile用于解决变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性。

ThreadLocal简介

  • 每一个线程都有一个自己的专属本地变量。
  • 实现原理 每个Thread都有一个单独的TheadLocalMap实现

ThreadLocal内存泄漏问题

  • ThreadLocalMap是弱引用,value是强引用,所以ThreadLocal没有被外部强引用的情况下,在垃圾回收的时候会key会被清理掉,而value不会被清理掉,就会导致key为null value不为null的Entry就会导致内存泄漏 ThreadLocalMap实现已考虑了这种情况,set get remove方法会清理掉key为null的记录。

强引用、弱引用、软引用、幻象引用

  • 强引用, 就是普通的new
  • 软引用 new SoftReference<Object>(obj); 当内存空间足够的时候,垃圾回收器不会回收它。 当内存空间不足时会回收它。
  • 弱引用 WeakReference<Object> wf = new WeakReference<Object>(obj); GC时候不管引用不引用都会回收它。
  • 幻象引用 PhantomReference<Object> pf = new PhantomReference<Object>(obj); 用来检测对象是否被回收。
  • 所有引用类型都是抽象类java.lang.ref.Reference的子类,子类里提供了get()方法。通过上面的分析中可以得知,除了幻象引用(因为get永远返回null),如果对象还没有被销毁,都可以通过get方法获取原有对象。其实有个非常关键的注意点,利用软引用和弱引用,我们可以将访问到的对象,重新指向强引用,也就是人为的改变了对象的可达性状态。所以对于软引用、弱引用之类,垃圾收集器可能会存在二次确认的问题,以确保处于弱引用状态的对象没有改变为强引用。
  • 但是有个问题,如果我们错误的保持了强引用(比如,赋值给了static变量),那么对象可能就没有机会变回类似弱引用的可达性状态了,就会产生内存泄露。所以,检查弱引用指向对象是否被垃圾收集,也是诊断是否有特定内存泄露的一个思路,我们的框架使用到弱引用又怀疑有内存泄露,就可以从这个角度检查。

为什么要用线程池

  • 降低资源消耗 通过重复利用已创建的资源 避免创建和销毁的消耗
  • 提高响应速度 当任务到达时,任务不需要等待线程创建就能立即执行。
  • 提高线程的可管理性 使用线程池可以进行统一的分配、调优和监控。

Runable 和 Callable接口的区别

  • Runable不会返回结果 而 Callable会返回结果。

执行excute方法和submit方法的区别是什么

  • excute用于提交不需要返回值得任务,无法判断任务是否被线程池执行成功与否
  • submit方法用于提交需要返回值得任务,线程池会返回一个Future对象,可以通过这个对象判断是否执行成功可以通过get方法来获取值,get()方法会阻塞当前线程直到任务完成。方法则会阻塞当前线程一段时间后立即返回,有可能未执行完

如何创建线程池

  • 通过ThreadPoolExecutor创建
  • 利用Executors返回线程池对象的弊端如下

    • FixedThreadPool和SingleThreadExecutor 允许请求的队列长度为Integer.MAX_VALUE 可能堆集大量的请求,从而导致OOM
    • CachedThreadPool和ScheduledThreadPool 允许创建的线程数量为Integer.MAX_VALUE 可能会创建大量线程,导致OOM

Atomic原子类

Atomic指一个操作是不可中断的,即使是在多个线程一起执行时,一个操作一旦开始,就不会被其他线程干扰。

JUC包中的原子类有哪四类?

  • AtomicInteger
  • AtomicLong
  • AtomicBoolean
  • AtomicIntegerArray
  • AtomicLongArray
  • AtmoicReferenceArray
  • AtmoicReference
  • AtmoicStampedReference
  • AtmoicMarkableReference
  • AtmoicIntegerFieldUpdater
  • AtmoicLongFieldUpdater
  • AtmoicStampedReference

Atmoic原理?

  • 利用CAS(Compare And Swap) + Volatile 和native方法 保证原子操作,从而避免synchronized的高开销,执行效率大为提升。

AQS

是一个用来构建锁和同步器的框架,使用AQS能简单且高效的构造出应用广泛的大量的同步器,ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等都是基于AQS的。

AQS原理

  • 如果资源时空闲的话则将当前请求资源线程设置为有效的工作线程,且资源被设置为锁定状态,如果被请求的共享资源被占用,那么久需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是利用CLH队列锁实现的。

AQS对资源的共享方法

  • Exclusiv 独占 只有一个线程能执行,又可分为公平锁和非公平锁。
  • Share共享 多个线程可同时执行

AQS组件总结

  • Semaphore 信号量 允许多个线程同时方法
  • CountDownLatch 倒计时器 可以让某一个线程等待直到倒计时结束,再开始执行
  • CycliBarrier 循环栅栏 让一组线程全部到达屏障后屏障才会开门。

说说List,Set,Map三者的区别

  • List 存储的值可重复,有序的对象
  • Set 不允许重复的值
  • Map 使用键值对存储 不能有重复的键

ArrayList与LinkedList区别

  • 是否保证线程安全 ArrayList 和 LinkedList 都是不同步的
  • 底层数据结构 ArrayList底层用的是Object数组,LinkedList用的是双向链表JDK1.6之前为循环链表 JDK1.7取消了循环
  • 插入和删除是否受位置的影响 ArrayList采用数组存储所以插入和删除都要移动数组内的元素O(n), 但是LinkedList的话只需要更改前后指针即可O(1)。
  • 是否支持快速随机访问 ArrayList支持 LinkedList不支持
  • 内存空间占用 ArrayList的空间浪费主要体现在list列表尾部要预留一定的容量空间,而LinkedList每个元素要多花费一点元素来存储前驱和后驱的指针。

ArrayList与Vector的区别

  • Vector线程安全 ArrayList不线程安全

HashMap和HastTable的区别

  • HashMap非线程安全 HashTable内部有synchronized修饰,但是需要线程安全还是推荐用ConcurrentHashMap
  • 效率 HashMap效率比table高(因为synchronized)
  • HashMap中Null可以作为键,HashTable用null则会抛出NullPointerException
  • HashTable默认大小为11每次扩充为2n+1 HashMap默认为16 之后每次扩充2n
  • 给定了预留大小HashTable则直接初始化预留大小 而HashMap则会初始化2倍
  • 底层数据结构 JDK 1.8以后HashMap在哈希冲突链表大于8时会转化成红黑树 HashTable没有

HashMap和HashSet区别

  • HashSet底层基于HashMap

HashSet如何检查重复

调用hashCode() 判断是否存在 如果存在则调用equals如果相等则不会添加成功。

HashMap的长度为什么是2的幂次方

因为hash的值范围在int,long……等基础类型上 这些基础类型都是2的n次方
而数组下标计算方法时(n-1)&hash 所以HashMap的长度是2的幂次方

HashMap多线程操作导致死循环问题

主要原因在于并发下的rehash会造成元素之间形成一个循环链表,jdk1.8解决了这个问题

ConcurrentHashMap和Hashtable区别

  • 底层数据结构 ConcurrentHashMap采用 数组+链表 1.8之后采用跟HashMap
  • 实线程安全的方式: ConcurrentHashMap在1.8之前采用分段 1.8之后采用node加synchronized+cas来操作性能提升了(1.6对synchronized锁做了很多优化) 而HashTable则采用同一把锁。

comparable和Comparator区别

  • comparable 用于编写类的默认比较方法 常用类与类之间的比较
  • Comparator 常用于排序之间的比较,灵活运用可以实现多个不同的排序规则。

集合框架数据结构

  • List:

    • ArrayList: Object数组
    • Vector: Object数组
    • LinkedList: 双向链表
  • Set:

    • HashSet: HashMap
    • TreeSet: 红黑树
  • Map

    • HashMap
    • LinkedHashMap 在HashMap基础上增加了双向链表,可以通过对链表进行相应的操作,实现了访问顺序的相关逻辑
    • Hashtable: 数组+链表
    • TreeMap: 红黑树

  1. JAVA中的几种基本数据类型是什么,各自占用多少字节?
    基本数据类型有:

    • byte 1字节
    • char 2字节
    • short 2字节
    • int 4字节
    • long 8字节
    • float 4字节
    • double 8字节
    • boolean 1字节
  2. String能被继承吗,为什么?
    不能因为String类被修饰为Final 所以无法继承。
  3. String, StringBuffer, StringBuilder的区别
    String各属性被定义为Final不可更改 所以常见的字符串拼接需要重新实例化String 开销极大 常见的字符串拼接会采用StringBuffer,StringBuilder StringBuffer是线程安全的 StringBuilder不是。
  4. ArryList 和 LinkedList区别

    • ArrayList底层是基于数组实现的 初始化时候会给数组一个初始值 如果大于这个容量则会重新实例化一个数组 把原来的数组复制过去 但是读取的话是直接跳过去读时间复杂度O(1) 添加和删除需要移动右边的元素 最坏情况O(n) 常用语读取多 写入少的场景。
    • LinkedList是基于链表实现的, 所以读取的话 最坏情况要遍历所有元素时间复杂度O(n), 而添加和删除只需要O(1) 适合读取少 写入多的场景。
  5. 类的实例化顺序
    先初始化静态变量和静态块,然后是非静态变量和非静态代码块,然后是构造器。由于静态成员只会被初始化一次,所以如果静态成员已经被初始化过,将不会被再次初始化。
  6. HashMap是线程安全吗,并发下使用的Map是什么,他们的内部原理是什么,比如存储方式,hashcode,扩容,默认容量
    HashMap不是线程安全,并发下使用ConcurrentHashMap 内部原理是hash表+链表解决冲突 单个链表大于阈值8则会变成红黑树进行维护以应对极端情况下HashMap退化成链表O(n)
  7. JAVA8的ConcurrentHashMap为什么放弃了分段锁
    JDK1.8中ConcurrentHashMap采用HashTable方式去维护Map相比于1.7的分段锁 1.8中的锁更加细粒度的上锁以提高性能,并且大量使用了CAS去替换掉部分锁减少开销。底层跟HASHMAP原理差不多也是用链表法解决冲突 当大于阈值会转化成红黑树维护。
  8. 有没有有顺序的MAP实现类,如果有,他们是怎么保证有序的。
    TreeMap,LinkedHashMap(TreeMap默认是key升序,LinkedHashmap默认是数据插入顺序)

TreeMap是基于比较器Comparator来实现有序的。
LinkedHashmap是基于链表来实现数据插入有序的。

  1. 抽象类和接口的区别,类可以继承多个类么,接口可以继承多接口么,类可以实现多个接口吗?

    1. 抽象类子类可以选择实现,而接口则是必须实现,类可以实现多个接口 接口可以继承多个接口 但是类不可以继承多个类
    2. 接口可以继承接口,抽象类不可以继承接口,但可以实现接口。
    3. 抽象类可以继承实体类。抽象类可以实现(implements)接口,抽象类是否可继承实体类,但前提是实体类必须有明确的构造函数。
  2. 继承和聚合的区别在哪

    • 继承
      指的是一个类继承另外的一个类的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识。

    • 聚合
      聚合体现的是整体与部分、拥有的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期;比如计算机与CPU、公司与员工的关系等;

  3. BIO NIO AIO的区别
    同步、异步:IO数据已到达内核 应用层停下来等待内核传输完数据到应用层(同步) 还是数据传输到应用层了再通知线程(异步)

阻塞、非阻塞: IO数据无到达机器 线程轮询等待(阻塞) IO数据无到达机器线程直接返回 如到达机器再通知线程(非阻塞)
BIO:同步阻塞 弟中弟
NIO:同步非阻塞
AIO:异步非阻塞
NIO、与AIO应用场景
NIO适合在数据量小IO较下的场景
AIO适合在数据量大IP较大的场景。

  1. 反射的原理,反射创建类实例的三种方式是什么
    JAVA语言编译之后会生成一个.class文件,反射就是通过字节码文件找到某一个类、类中的方法以及属性等。

    //第一种表示方式--》实际在告诉我们任何一个类都有一个隐含的静态成员变量class
    Class class1 = Foo.class;

    Class class2 = foo1.getClass();

    //第三种表达方式
    Class class3 = null;
    try {

    class3 = Class.forName("com.imooc.reflect.Foo");

    } catch (ClassNotFoundException e) {

    e.printStackTrace();

    }
    //
    System.out.println(class2==class3);//true

    //我们完全尅通过类的类类型创建该类的对象实例--》通过class1 or class2 or class3
    //创建Foo类的实例对象
    try {

    //需要有无参数的构造方法
    Foo foo = (Foo) class1.newInstance();//需要强转
    foo.print();

    } catch (Exception e) {

    e.printStackTrace();

    }

  2. 反射中,Class.forName和ClassLoader的区别
    类装载有以下过程:

    • 装载:通过累的全限定名获取二进制字节流,将二进制字节流转换成方法区中的运行时数据结构,在内存中生成Java.lang.class对象;
    • 链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
    • 校验:检查导入类或接口的二进制数据的正确性;(文件格式验证,元数据验证,字节码验证,符号引用验证)
    • 准备:给类的静态变量分配并初始化存储空间;
    • 解析:将常量池中的符号引用转成直接引用;
    • 初始化:激活类的静态变量的初始化Java代码和静态Java代码块,并初始化程序员设置的变量值。

    Class.forName会对static块等进行初始化而ClassLoader不行 所以jdbc等就需要用Class.forName来进行实例因为含有静态代码块。

  3. 描述动态代理的几种实现方式,分别说出相应的优缺点:

    • JDK动态代理(需要继承接口实现)
    • CGLIB基于ASM的字节码处理实现动态代理 无需继承接口。
  4. final的用途
    防止子类继承或更改此属性/方法。
  5. 写出三种单例模式实现

    //懒汉式单例
    public class Singleton {
        private static Singleton singleton;
        private SDingleton() {}//防止实例初始化
        public static synchronized Singleton getInstance(){
            if(singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    优点:用到才初始化
    缺点:初始化并发性能较低
//饿汉式单例
public class Singleton {
    private Singleton() {} //防止实例
    private static final Singleton SINGLETON = new Singleton();
    public static Singleton getInstance(){
        return SINGLETON;
    }
}
    优点:没有加锁,效率执行高。
    缺点:类加载时就初始化 浪费内存。
//登记式模式
public class Singleton{
    private Singleton() {}
    public static Singleton getInstance(){
        return Holder.SINGLETON;
    }
    private static class Holder{
        private static final Singleton SINGLETON = new Singleton();
    }
}
优点 包含所有。

17. JDK和JRE的区别
    - JDK是功能齐全的Java SDK拥有JRE所拥有的一切,还有编译器(javac)和工具(javadoc和jdb)能够创建和编译程序
    - JRE 是Java运行时环境。它是运行已编译Java程序所需的所有内容的集合,包括Java虚拟机(JVM),Java类库,Java命令和其他的一些基础构件。但是它不能用于创建新程序。

18. JAVA和C++的区别
    - 都是面向对象的语言,都支持封装、继承、多态
    - Java不提供指针来直接访问内存,程序内存更安全
    - Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承;
    - Java有自动内存管理机制,不需要程序员手动释放无用内存。

19. 重载和重写的区别?构造器Constructor是否可被override?
    - 重载 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同、返回值和访问修饰符可以不同
    - 重写 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类访问修饰符为private则子类就不能重写该方法。

20. String str="abcd" 与 String str1 = new String("abcd") 一样吗?
这两种不同的创建方法是有区别的。
    - 第一种先检查字符串常量池中有没有"abcd",如果字符串常量池中没有,则创建一个,有则将str指向字符串常量池的abcd
    - 第二种方式是直接在堆内存空间创建一个新的对象
    所以不相等 因为一个是堆内存中的String对象 一个是常量池的String对象。

21. == 与 equals区别
    - ==是比较内存地址
    - equals 默认有两种情况
        - 情况1: 类没有覆盖equals方法 则通过equals比较时等价于通过==比较这两个对象
        - 情况2: 覆盖了equals方法则调用覆盖的方法
22. hashCode与equals
    - hashCode作用就是获取哈希码通常在HashMap、HashSet中运用到,通过hashCode来计算散列码。 如果在一个哈希表里有相同的HashCode的话则会调用equals方法判断是否相等 如果不相等则说明出现了哈希冲突则调用具体的哈希解决方法(拉链法、…………)
23. hashCode()与equals()的相关规定
    1. 如果两个对象相等,则hashcode一定也是相同
    2. 两个对象相等,对两个对象分别调用equals方法都返回true
    3. 两个对象有相同的hashcode值,它们也不一定是相等的
    4. 因此equals方法被覆盖过,则hashCode方法也必须被覆盖
    5. hashCode的默认行为是堆上的对象产生独特值,如果没有重写hashCode,则该class的两个对象无论如何都不会相等。
24. 静态编译和动态编译
    - 静态编译:在编译时确定类型,绑定对象
    - 动态编译:运行时确定类型,绑定对象
25. 反射机制优缺点
    - 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
    - 缺点: 性能瓶颈,反射相当于一系列解释操作,通知JVM要做的事情,性能比直接的java代码要慢很多。

1. Include 与 require的区别,require和require_once的效率哪个高?
PHP在遇到include时就解释一次,如果页面中出现10次include,php就解释10次,而php遇到require时只解释一次,即使页面出现多次require也只解释一次,因此require的执行表率比include高。

Php使用require包含文件时将被包含的文件当成当前文件的一个组成部分,如果被包含的文件中有语法错误或者被包含的文件不存在,则php脚本将不再执行,并提示错误。

Php使用include包含文件时相当于指定了这个文件的路径,当被包含的文件有语法错误或者被包含的文件不存在时给出警告,不影响本身脚本的运行。

Include在包含文件时可以判断文件是否包含,而require则不管任何情况都包含进来。

Require的效率比require_once的效率更高,因为require_once在包含文件时要进行判断文件是否已经被包含。

2. Cookie和session的区别,禁止了cookie后session能正常使用吗?session的缺点是什么?session在服务器端是存在哪里的?是共有的还是私有的?

COOKIE保存在客户端,用户通过手段可以进行修改,不安全,单个cookie允许的最大值是3k。而SESSION保存在服务器端,相对比较安全,大小没有限制。禁用了cookie之session不能正常使用。

Session的缺点:保存在服务器端,每次读取都从服务器进行读取,对服务器有资源消耗。

Session保存在服务器端的文件或数据库中,默认保存在文件中,文件路径由php配置文件的session.save_path指定。

Session文件是公有的。

3. 怎么防止sql注入?

1、过滤掉一些常见的数据库操作关键字:select,insert,update,delete,and,*等

或者通过系统函数:addslashes(需要被过滤的内容)来进行过滤。

2、在PHP配置文件中

Register_globals=off;设置为关闭状态 //作用将注册全局变量关闭。

比如:接收POST表单的值使用$_POST['user'],如果将register_globals=on;直接使用$user可以接收表单的值。

3、SQL语句书写的时候尽量不要省略小引号(tab键上面那个)和单引号

4、提高数据库命名技巧,对于一些重要的字段根据程序的特点命名,取不易被猜到的

5、对于常用的方法加以封装,避免直接暴漏SQL语句

6、开启PHP安全模式

Safe_mode=on;

7、打开magic_quotes_gpc来防止SQL注入

Magic_quotes_gpc=off;默认是关闭的,它打开后将自动把用户提交的sql语句的查询进行转换,把'转为',这对防止sql注入有重大作用。

因此开启:magic_quotes_gpc=on;

8、控制错误信息

关闭错误提示信息,将错误信息写到系统日志。

9、使用mysqli或pdo预处理。

4. 数据库索引有几类,分别是什么?什么时候该用索引

 普通索引、主键索引、唯一索引

  并非所有的数据库都以相同的方式使用索引,作为通用规则,只有当经常查询列中的数据时才需要在表上创建索引。

5. 引用传值和非引用传值的区别,什么时候该用引用传值?什么时候该用非引用传值?

按值传递:函数范围内对值的改变在函数外都会被忽略。

按引用传递:函数范围内对值的任何改变在函数外也将反应出这些修改。

按值传递时,php必须复制值,如果操作的是大型的对象和字符串,这将是一个代价很大的操作。按引用传递不需要复制值,因此对性能的提高有好处。

当需要在函数内改变源变量的值时用引用传递,如果不想改变原变量的值用传值。

6. 写几个魔术方法并说明作用?

__call()当调用不存在的方法时会自动调用的方法

__autoload()在实例化一个尚未被定义的类是会自动调用次方法来加载类文件

__set()当给未定义的变量赋值时会自动调用的方法

__get()当获取未定义变量的值时会自动调用的方法

__construct()构造方法,实例化类时自动调用的方法

__destroy()销毁对象时自动调用的方法

__unset()当对一个未定义变量调用unset()时自动调用的方法

__isset()当对一个未定义变量调用isset()方法时自动调用的方法

__clone()克隆一个对象

__tostring()当输出一个对象时自动调用的方法

7. $_REQUEST、$_POST、$_GET、$_COOKIE、$_SESSION、$_FILE的意思是什么?

它们都是PHP预定义变量。

$_REQUEST用来获取post或get方式提交的值

$_POST用来获取post方式提交的值

$_GET用来获取get方式提交的值

$_COOKIE用来获取cookie存储的值

$_SESSION用来获取session存储的值

$_FILE用来获取上传文件表单的值

8. 数组中下标最好是什么类型的,为什么?

数组的下标最好是数字类型的,数字类型的处理速度快。

  1. ++i和i++哪一个效率高,为什么?

++i效率比i++的效率更高,因为++i少了一个返回i的过程。

10.magic_quotes_gpc()、magic_quotes_runtime()的意思是什么?

Magic_quotes_gpc()是php配置文件中的,如果设置为on则会自动POST,GET,COOKIE中的字符串进行转义,在'之前加\

Magic_quotes_runtime()是php中的函数,如果参数为true则会数据库中取出来的单引号、双引号、反斜线自动加上反斜杠进行转义。

11.Echo()、print()、print_r()的区别?

Echo() 是PHP语法,可以输出多个值,不能输出数组。

Print() 是php的语言结构,可以输出单个简单类型的变量值。

Print_r() 是php函数,可以打印出复杂类型变量的值,如数组,对象。

12.谈谈你对Mvc的认识

MVC是一种设计模式,强制使输入、处理、输出分开,MVC的三个核心部分:M模型,V视图,C控制器。

视图就是用户看到并与之交互的界面。

模型就是程序的数据业务规则。

控制器接收用户的数组调用模型和视图去完成用户需求。

使用MVC的优点:低耦合、高重用性、较低的生命周期成本、快速开发部署、可维护性、可扩展性,有利于软件工程化管理。

MVC的缺点:没有明确的定义,完全理解并不容易。小型项目不适合用MVC。

13.框架中什么是单一入口和多入口,单一入口的优缺点

多入口就是通过访问不同的文件来完成用户请求。

单一入口只web程序所有的请求都指向一个脚本文件的。

单一入口更容易控制权限,方便对http请求可以进行安全性检查。

缺点:URL看起来不那么美观,特别是对搜索引擎来说不友好。

14.打印一个用‘.’链接的字符串时候,还可以用什么代替‘.’链接效率更高些?

可以用,代替.,效率更高。

15.提示类型200、404、502是什么意思?

200是请求成功,404是文件未找到,502是服务器内部错误。

16.编写一个自定义函数提取这段路径的的后缀名。

“Www/hello/test.php.html?a=3&b=4”

Function geturltype($url){

$info=parse_url($url);

Return end(explode('.',$info['path']));

}

17.你对Memcach的理解,优点有哪些?

Memcache是一种缓存技术,在一定的时间内将动态网页经过解析之后保存到文件,下次访问时动态网页就直接调用这个文件,而不必在重新访问数据库。使用memcache做缓存的好处是:提高网站的访问速度,减轻高并发时服务器的压力。

Memcache的优点:稳定、配置简单、多机分布式存储、速度快