2025Java面试真题+八股文解析(持续更新)

1、CAS你知道吗?

CAS(compireAndSwap)比较并交换,它体现了乐观锁的一种思想,在无锁的情况下保证线程操作数据的原子性,它的内部存在3个操作数

1、变量内存值V

2、旧的预期值A

3、准备设置的新值B

当执行CAS指令时,只有当V=A时,才会去执行B更新V的值,否则不会更新

多个线程同时使用CAS去操作一个变量时,只有一个线程会执行成功,其他线程均会失败,然后会重新尝试或将线程挂起(阻塞)

另外,CAS是一种系统原语,它的执行一定是连续不被中断的,也就不存在并发问题,这样就保证了原子性

CAS虽然能很高效的解决原子操作,但是仍然存在问题

  • ABA问题 因为CAS只是判断获取值和在操作时这个值之间的时间该没改变来进行操作,当在这个时间内如果有一个操作修改了这个内存变量的值,由A改为B再改为A,这时CAS会认为这个值从来没有变过,但是值其实已经发生了一次改变

  • 循环时间长时开销大 因为底层是自旋锁,当操作迟迟无法完成的时候,会对CPU带来非常大的开销

  • 只能保证一个共享变量的原子操作 当对多个共享变量进行原子操作时,循环CAS就无法保证操作的原子性

2、什么是AQS?

AQS全称abstractQueueSynchronizer,即抽象队列同步器,是一种锁机制,它是作为一个基础框架使用的,像Reentrantlock,countdownlatch都是基于AQS实现的

AQS内部维护了一个先进先出的双向队列,队列中存储了排队的线程

AQS还维护了一个state,表示锁的状态,0为无锁状态,1为有锁状态,如果一个线程将state修改为1,就相当于当前线程获得了资源

对state的修改使用cas操作,保证多线程下的原子性

3、ReentrantLock底层原理是什么

ReentrantLcok意为可重入锁,和synchronized一样,当一个线程已经获得锁,再去尝试获得锁时,不会阻塞而是直接获得

ReentrantLock的底层是基于CAS+AQS实现,AQS的底层维护了一个state和一个双向队列,当有线程来抢锁时,会使用cas的方法来对state进行修改,如果修改成功,就将ReentrantLock中的ownerTread属性指向当前线程,如果修改失败,就会插入到双向队列的队尾

ReentrantLock支持公平锁和非公平锁两种实现,如果使用无参构造器,就是非公平锁,也可以传参true设置为公平锁。

篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

4、悲观锁、乐观锁和分布式锁的实现和细节

悲观锁:认为线程安全问题一定会发生,所以在操作数据之前先获取锁,保证线程串行执行,例如synchronized,lock

细节:

  • 悲观锁适合插入数据

  • 锁的粒度要尽量小,只锁住需要串行执行的代码

  • 配合事务使用时,要先提交事务再释放锁

乐观锁:认为线程安全问题不一定会发生,因此不加锁,只是在操作数据前判断是否有其他线程对数据做了修改,如果没有被修改则说明线程安全,更新数据,反正说明出现了线程安全问题,可以重试或者返回异常,例如给表加字段version,cas操作

细节:

  • 乐观锁适合更新数据

  • 更新前要先查询version,更新时比较version是否相同

分布式锁:满足分布式系统或集群模式下多进程可见且互斥的锁,常见的实现有redis和zookeeper,redis通常利用setnx方法

细节:

  • 锁的误删,比如线程1拿到锁但是出现了阻塞导致锁自动释放,在线程2拿到锁后执行业务逻辑时,线程1反应过来,继续执行,最后将本已经不属于他的锁误删了

  • 锁的误删解决:设置锁的唯一标识,每个线程在获取锁时,设置锁的value为线程唯一标识(可以用uuid实现),释放锁时判断锁的value是否跟自身线程唯一标识一致,一致才能释放

5、什么是死锁,死锁产生的条件?如何避免死锁?

死锁就是一组互相竞争资源的线程,因为互相等待又互不相让资源,导致永久阻塞无法进行下去的情况

死锁产生的条件有四个:

  • 互斥条件:资源x和y只能分别被一个线程占用

  • 占有且等待:线程t1占有资源x后,等待资源y被释放,同时自己不释放资源x

  • 不可抢占:其他线程不能强行抢占线程t1占有的资源

  • 循环等待:线程t1等待线程t2占有的资源,线程t2等待线程t1占有的资源

避免死锁的方法(破坏对应的条件):

  • 一次性申请所有资源(破坏互斥条件)

  • 占有资源的线程,在申请其他资源时,如果申请失败,可以主动释放自己的资源(破坏占有且等待条件)

  • 按照顺序去申请资源,然后反序释放资源,破坏循环等待条件

6、ConcurrentHashMap底层原理

ConcurrentHashMap是在HashMap的数据结构上,增加了CAS操作和Synchronized互斥锁来保证线程安全,并且使用volatile关键字修饰了node中的next和val字段来保证多线程环境下某个线程新增或修改节点对于其他线程是立即可见的。

在进行添加操作时:

  1. 计算hash值,定位该元素应该添加到的位置

  2. 如果不存在hash冲突,即该位置为null,则使用CAS操作进行添加

  3. 如果存在hash冲突,即该位置不为null,则使用synchronized关键字锁住该位置的头节点,然后进行添加操作

7、线程池的核心参数和执行原理?

线程池的核心参数有七个:

  corePoolSize:核心线程数

  maximumPoolSize:最大线程数量,核心线程+救急线程的最大数量

  keepAliveTime:救急线程的存活时间,存活时间内没有新任务,该线程资源会释放

  unit:救济线程的存活时间的单位

  workQueue:工作队列,当没有空闲核心线程时,新来的任务会在此队列排队,当该队列已满时,会创建应急线程来处理该队列的任务

  treadFactory:线程工厂,可以定制线程的创建,线程名称,是否是守护线程等

  handler:拒绝策略,在线程数量达到最大线程数量时,实行拒绝策略    拒绝策略:

  线程池执行原理:

8、线程池常见的阻塞队列?

  常见的阻塞队列有:

    ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO

    LinkedBlockingQueue:基于链表的有界阻塞队列,FIFO

    两者区别(链表与数组的区别)

ArrayBlockingQueue

LinkedBlockingQueue

底层是数组

底层是链表

强制有界

默认无界,传递容量参数后有界

初始化时即创建好node对象数组

插入时才创建node对象

两把锁(头尾)

一把锁

  LinkedBlockingQueue的优点是锁分离,很适合生产和消费频率差不多的场景,这样生产和消费互不干涉的执行,能达到不错的效率

9、线程池核心线程数如何确定?

  没有固定答案,先设定预期,比如我期望的CPU利用率在多少,负载在多少,GC频率多少之类的指标后,再通过测试不断的调整到一个合理的线程数

  公式:

  分三种情况

  高并发,任务处理时间短 ——》CPU核数+1,减少线程上下文切换

  并发不高,任务处理时间长    IO密集型任务——》CPU核数*2+1

      读写多,DB操作多,CPU占用少,并且IO数据传输时,是不占用CPU的,所以就可以多释放CPU资源,给其他线程运行,

    CPU密集型任务——》CPU核数+1

      CPU占用高,如计算任务,视频解码任务,这些任务线程上下文切换开销大,所以要尽量减小开销,提高CPU效率

10、线程池的种类有哪些

  在JUC的Executor类中,提供了多种创建线程池的方法,主要有四种

  1、固定线程数的线程池

  核心线程数与最大线程数一样,没有救急线程

  阻塞队列是LinkedBlockingQueue,最大容量是Integer.MaxValue

  适合任务量已知,相对耗时的任务

  2、单例线程的线程池

  核心线程数和最大线程数都是1,没有救急线程

  阻塞队列是LinkedBlockingQueue,最大容量是Integer.MaxValue

  适合按顺序执行的任务,与单线程的区别是,单线程运行完了就会销毁,而线程池创建的线程运行结束不会销毁,而是等待下一个任务,可以重复使用,减少了创建线程和销毁线程的时间,提高资源利用率。

  3、可缓存的线程池

  核心线程数为0,最大线程数为Integer.MaxValue,救急线程存活时间为1分钟

  阻塞队列为SynchronousQueue:不存储元素的阻塞队列,每一个插入操作必须等待一个移除操作

  适合任务比较密集,且任务执行时间短的情况,因为使用的是救急线程,在一定时间没有新任务后就会销毁,节省资源,同时能应付任务密集的时间段。

4、可以定时执行的线程池

  核心线程数自定义,最大线程数为Integer.MaxValue

  适合需要定时执行任务的场景

11、线程池使用场景

1、批量将pgsql数据导入ES

  计算数据总条数——》固定每页200条,计算总页数N——》将页数N设置为CountdownLatch的count——》创建N个线程批量导入,每次导入完调用countdown方法——》主线程中调用await方法,线程都执行完后,主线程结束

2、异步调用

  用户搜索需要保存搜索记录,但保存功能不能影响正常搜索——》搜索时通过线程异步调用保存记录功能

  关键注释:@EnableAsync加在SpringBoot启动类上,@Bean将自定义线程池注入到容器中(核心8,最大8), @Async("线程池名称")加在需要异步调用的方法上,

12、对ThreadLocal的理解,ThreadLocal内存泄漏问题

ThreadLocal是java.lang包中的一个类,它实现了线程之间的资源隔离,让每个线程都有自己的独立资源

  ThreadLocal的底层实现的关键是它的静态内部类ThreadLocalMap

  • 每个Thread对象都有一个成员变量threadlocals,它指向一个ThreadLocalMap,该Map以ThreadLocal为key,需要存储的线程变量为value

  • 当调用ThreadLocal的set()方法时,会先拿到当前线程对象的ThreadLocalMap,然后以当前ThreadLocal为key将变量存储到map中,调用get方法和remove方法时也是通过这个key去操作。

  ThreadLocal内存泄漏问题:

    回答这个问题,我们首先要知道java中的四种引用

    强引用:强引用是最常见的,只要把一个对象赋值给一个引用变量,那么这个对象就被强引用了,强引用的对象只要不为null,就不会被回收

    软引用:软引用相对弱化一些,需要用softReference对象构造方法去创建软引用,当内存充足时,软引用的对象不会被回收,当不足时就会被回收

    弱引用:弱引用又更弱了一些,需要用weakReference的构造方法创建弱引用,当发送GC时,只要是弱引用的对象就会被回收

    虚引用:虚引用要配合引用队列使用,它的主要作用是跟踪对象垃圾回收的状态,当对象被回收时,通过引用队列做一些通知类工作

  在ThreadLocalMap中,Key ThreadLocal是一个弱引用,但是值value是一个强引用,当垃圾回收时,弱引用ThreadLocal会被回收,而value不会,这就导致value成了一个无法被访问也无法回收的变量,造成内存泄漏

  解决办法:当使用完ThreadLocal变量后,及时使用remove方法进行清除。

 
  1. static class Entry extends WeakReference<ThreadLocal<?>> {

  2. /** The value associated with this ThreadLocal. */

  3. Object value;

  4. Entry(ThreadLocal<?> k, Object v) {

  5. super(k);

  6. value = v;

  7. }

  8. }

三、JVM虚拟机

1、JVM的主要组成部分及其作用

  类加载器:将java字节码文件加载到内存中

  运行时数据区:就是我们常说的JVM的内存,我们的所有程序都被加载到这运行

  执行引擎:负责将字节码翻译成底层系统指令,然后交由系统执行

  本地方法接口:与本地库交互实现一些基础功能

2、运行时数据区的组成部分及其作用

  JVM运行时数据区由方法区(元空间),堆,程序计数器,虚拟机栈,本地方法栈组成

  • 方法区/元空间:方法区是一个线程共享的区域,里面存储了类信息,常量,静态变量等待信息,虚拟机启动时创建,虚拟机关闭时释放,内存无法满足分配请求时,会报OOMError:Metaspace

  • 堆:Java堆是一个线程共享的区域,里面存储了实例对象、数组等等,内存不够时抛出OOM异常

    • 堆分为年轻代和老年代

      • 年轻代被分为三块,eden区和两个严格相同的Survivor区

      • 老年代主要用来保存生命周期长的对象,主要是老的对象

  • 程序计数器:用来记录当前线程正在执行的字节码指令地址,是线程私有的

  • 虚拟机栈:虚拟机栈是java方法执行时的内存结构,线程私有,虚拟机会在每个java方法执行时开启一个栈帧,用于存储方法参数,局部变量,返回地址,操作数栈(中间计算结果,比如i+j的值)等信息,当方法执行完毕时,该方法会从虚拟机栈中出栈。

  • 本地方法栈:本地方法栈是线程私有的,为虚拟机使用的native方法提高服务

  如果栈的深度超过了虚拟机允许的最大深度,就会抛出StackOverflowError异常; 如果在扩展栈时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

3、垃圾回收是否涉及栈?

垃圾回收主要指的是堆内存,栈帧内存在栈帧出栈后就释放

4、栈内存越大越好吗?

每个虚拟机栈内存默认为1M

不一定,机器内存是固定的,栈内存越大,可以同时活动的栈帧就越少,效率反而降低

5、方法内部的局部变量是否线程安全?

只要局部变量没有离开方法的作用范围,就是线程安全的

如果局部变量引用了一个对象,并且逃离了方法的作用范围,就有可能线程不安全

6、什么情况下会栈溢出?

  栈帧过多:比如太多层级的递归调用

    栈帧过大:出现少

7、堆栈的区别是什么?

1、存放内容

堆中存放的是对象实例和数组,该区域更关注的是数据的存储,

(静态变量放在方法区,静态对象仍然放在堆中)

栈中存放的是局部变量,栈帧,操作数栈,返回结果等。该区更关注的是程序方法的执行。

然而实际上,对象并不总是在堆中进行分配的,这里就需要介绍一下JVM的逃逸分析技术了。JVM会通过逃逸分析技术,对于逃不出方法的对象,会让其在栈空间上进行分配。

2、程序的可见度

堆是线程共有的,栈是线程私有的。

3、异常错误

如果栈内存没有可用的空间存储方法调用和局部变量,JVM会抛出java.lang.StackOverFlowError。而如果是堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError

   4、物理地址

堆的物理地址是不连续的,性能相对较慢,是垃圾回收区工作的区域。在GC时,会考虑物理地址不连续,而使用不同的算法,比如复制算法,标记-整理算法,标记-清除算法等。

栈中的物理地址是连续的,LIFO原则,性能较快。

5、内存分别

堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定,一般堆大小远远大于栈。

栈是固定大小的,所以在编译期就确认了。

篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取

8、运行时常量池是什么?和字符串常量池的区别

运行时常量池是方法区的一部分,它可以看做是一张表,用于存放编译器生成的各种字面量和符号引用,在类被加载时,它的常量池信息就会被放入运行时常量池,并且还会保存符号引用对应的直接引用。

而字符串常量池是存放在堆里的,在HotSpot虚拟机中,它的底层是一个c++的hashtable,它将字符串的字面量作为key,实际堆中创建的String对象的引用作为value。

当创建一个String对象时,会拿着字面量尝试在字符串常量池中获取对应String对象引用,如果是首次执行,就会在堆中创建一个String对象,并保存到字符串常量池中,然后返回。

9、什么是直接内存?

  直接内存不属于JVM内存,是操作系统的内存,常见于NIO操作,用于数据缓冲区,拥有较高的读写性能,且不受JVM内存回收影响

  BIO(同步阻塞IO)

    发送请求后线程一直阻塞,直到数据处理完并返回

    NIO(同步非阻塞IO)

    通过一个线程轮询大量socket,当有socket准备就绪时通知客户端,客户端调用函数接收。

    AIO(异步非阻塞IO)

  • 每个请求都会绑定一个Buffer;

  • 通知操作系统去完成异步的读(这个时间你就可以去做其他的事情)

  • 读完之后会通知客户端来读取数据

10、对象什么时候可以被垃圾回收?如何标记垃圾?

  当没有任何一个强引用指向对象时,对象就可以被垃圾回收

  主流的虚拟机一般通过可达性分析算法分析一个对象是否能被回收,也有系统采用引用计数法判断

  可达性分析算法:

  1、算法主要思想是先确定一些肯定不能被回收的对象作为GCRoot,    GCRoot对象可以是:

      虚拟机栈中引用的对象

      本地方法栈中Native方法引用的对象

      方法区中静态属性,常量引用的对象

  2、然后以GCRoot为根节点,去向下搜索,找到它们直接引用或间接引用的对象

  3、在遍历完之后,如果发现有一些对象不可达,那么就认为这些对象已经没用用了,需要被回收

  引用计数法:

  就是为每一个对象添加一个引用计数器,用来统计指向当前对象的引用次数,一旦这个引用计数器变为0,就意味着它可以被回收了。

11、垃圾回收算法有哪些?

1、标记清除算法:标记清除算法分为两个步骤,标记-清除

  标记:遍历内存空间,对需要被回收的对象打上标记,通常使用可达性分析算法

  清除:再次遍历空间,将被标记的内存进行回收

  缺点:

  遍历两次,效率低

  因为清除后没有进行整理,容易形成碎片化不连续的的内存空间

  2、标记整理算法:标记整理算法分为三个步骤,标记-整理-清除

  标记同上

  整理:将所有存活的对象压到内存的一端,并按顺序排列

  清除掉其他空间

  缺点:

  效率低,速度慢

  3、复制算法:将内存分为等大的两块,每次只使用其中一块,当触发GC时,将存活的对象全部移到另一块内存的一端,然后将当前内存空间一次性回收,如此循环往复

  缺点:内存利用率低

12、什么是分代收集算法

在java8中,内存被分为两份:新生代和老年代

新生代内存使用复制算法,老年代内存使用标记整理算法

当对象在新生代内存中经历了一定的垃圾回收后,它将被晋升到老年代。

分代收集算法重复利用了对象生命周期的特点,提高了回收效率

大对象直接进入老年代

什么是大对象呢,这个是由jvm定义的参数值决定的,但是这个参数只在Serial和ParNew垃圾收集器中生效 :-XX:PretenureSizeThreshold

当我们新分配的对象大小大于等于这个值,就会直接在老年代中分配

长期存活的对象将进入老年代

在每个对象的头信息中,都包括一个年龄计数器

对象在经过一次minor gc之后,如果仍然存活,并且能够被 survior所容纳 ,那么这个年龄计数器就会加一,当计数器的值达到了默认值大小(一般默认值为15),就会进入到老年代。

对象动态年龄判断后决定是否进入老年代

当survior区域的存活对象的总大小占用了survior区域大小的50%(可以通过参数指定),那么此时将按照这些对象的存活年龄从从到大排序,然后依次累加,当累加到对象大小超过50%,则将大于等于当前对象年龄的存活对象全部挪到老年代。

详细解释:

首先要知道Minor GC 和 Full GC的区别

普通GC(minor GC):只针对新生代区域的GC,指发生在新生代的垃圾回收动作,因为大多数Java对象存活率都不高,所以Minor GC非常频繁,一般回收速度也比较快。

全局GC(major GC or FullGC) :指发生在老年代的垃圾收集动作,出现了Major GC,经常会伴随至少一次的MinorGC(不是绝对的)。Major GC的速度一般要比Minor GC慢10倍以上。

该算法将内存分为新生代、老年代、新生代中又分伊甸园、幸存区from、幸存区to,对象创建之初,会存在于伊甸园中,当伊甸园满了之后,会触发一次Minor GC,伊甸园中还存活的对象会被转入幸存区from,当伊甸园再次触发Minor GC时,GC会扫描伊甸园和幸存区from,对这两个区进行垃圾回收,垃圾回收后,幸存区from中还存活的对象利用复制清除算法复制到幸存区to(如果有对象的年龄达到了老年代区,则复制到老年代区),复制完之后把幸存区from清除掉,之后把from区和to区交换位置(from变to,to变from,保证幸存区to为空),最后把伊甸园中存活下来的对象放入幸存区from,(如果有对象的年龄达到了老年代区,则复制到老年代区),同时把这些对象的年龄+1即可。

13、垃圾收集器有哪些?

串行收集器:GC时只会有一个线程在工作,Java应用中的线程都要STW,等待垃圾回收结束

并行收集器:GC时会有多个线程参与垃圾收集,Java应用中的线程都要STW,等待垃圾回收结束,JVM默认

CMS(Concurrent Mark Sweep)收集器:并发收集器,进行垃圾回收时不会暂停Java应用线程,STW时间短,CMS收集器采用的是标记清除算法,所以不需要移动存活对象的位置,GC可以和Java应用程序同时运行

G1(Garbage-First)收集器:基于区域划分的收集器,适用于大内存应用。

14、什么是G1垃圾收集器

G1是JDK9默认的垃圾收集器,代替了CMS收集器。它的目标是达到更高的吞吐量和更短的GC停顿时间。

G1垃圾回收器将堆内存划分为多个区域,每个区域都可以充当Eden,Survivor,old,humongous(专为大对象准备)

G1垃圾回收过程可以分为几个主要阶段:

  1、初始标记:标记GCRoot直接关联的对象,需要STW

  2、并发标记:对GCRoot开始对堆中对象进行并发标记,需要STW

  3、重新标记:解决一些漏标错标问题,需要STW

  4、混合收集:将不需要回收的eden,Survivor对象放入新的Survivor区,old对象放入新的old区,如果Survior对象达到老年年龄后也会放入新old区。然后将区域内存回收

  要注意的是,混合收集不会处理所有区域,而是根据停顿时间目标去筛选出回收价值高(存活对象少)的区域。

15、什么是类加载器?类加载器有哪些?

类加载器是一个负责加载类的对象,因为JVM虚拟机只能运行二进制文件,类加载器的作用就是将字节码文件加载到JVM中运行。

JVM中有三个内置的类加载器

  BootStrapClassLoader(启动类加载器):最顶层的加载器,由C++实现,主要用来加载Java的核心类裤(JAVA_HOME/lib)

  ExtensionClassLoader(扩展类加载器) :主要负责加载JAVA_HOME/lib/ext下的扩展类

  AppClassLoader(应用程序类加载器) :主要负责加载ClassPath下的类,也就是开发者自己写的Java类

另外,还有自定义ClassLoader,通过继承ClassLoader实现。

16、什么是双亲委派模型?为什么类加载采用双亲委派?

当一个类加载器尝试加载一个类时,会先委托上一级加载器去加载,如果上一级还有上级,就会委托上一级的加载器的上级去加载,如果所有上级加载器都不能加载该类,则类加载器尝试自己加载类。

作用:

  1、防止重复加载:防止已经在上级加载器中加载的类重复地被子加载器再次加载

  2、为了安全,防止核心类库API被篡改

  3、保证类的一致性

17、类装载的执行过程是怎样?

1、加载

  加载过程需要完成三步

  • 通过类的全限定名来获取类的二进制流

  • 解析类的二进制流为方法区的类数据结构(Java类模型)

  • 在堆中创建一个该类的Class对象,作为方法区中该类的数据访问入口。

2、验证

  验证类是否符合JVM规范

    ①文件格式验证

    ②元数据验证

    ③字节码验证

    ④符号引用验证:检查符号引用对应的类或方法是否在常量池中存在

3、准备

  该阶段为类的静态变量分配内存,并设置初始值,这里所说的初始值“通常情况”下是数据类型的零值

4、解析

  将常量池的符号引用替换为直接引用

  符号引用:以一组符号来描述所引用的目标,有时候JVM不知道引用的类或者方法内存地址在哪,于是就先用符号引用代替直接引用

  直接引用:直接指向目标的指针

5、初始化

  对类的静态变量,静态代码块进行初始化操作

//6、使用:使用类执行代码

//7、卸载:用户代码执行完毕后,销毁Class对象

18、用于JVM调优的参数有哪些?

1、设置堆空间大小

  -Xms 512m 设置初始化大小,默认是物理内存的1/64

  -Xmx 1g 设置最大大小,默认是物理内存的1/4

  2、虚拟机栈大小

  -Xss 256k 默认1M

  3、年轻代eden区和survivor区大小比例

  -XXSurvivorRatio 8 表示eden区:survivor区=8:2

  4、年轻代晋升到老年代阈值

  -XX:MaxTenuringThreshold=10 默认为15

  5、设置垃圾回收器

  -XX:+Use[垃圾回收器名]

19、JVM调优的工具有哪些?

命令:

  jsp 进程状态信息

  Jstack 线程内的堆栈信息

  Jmap(使用较多) 查看堆的信息,堆的大小配置,堆内存各部分的使用情况

  jstat 垃圾回收信息

  工具:

  jconsole:用于对JVM内存,线程,类的监控

  visualVM:用于对JVM内存,线程,类的监控

20、Java内存泄漏的排查思路?

首先通过jmap命令或者设置JVM参数区获取dump文件

然后将dump文件放入VisualVM中去分析

通过查看堆的信息,定位到哪行代码出了问题,然后再去修改代码

21、CPU彪高排查方案与思路

1、使用top命令,查看哪个进程CPU使用率较高

2、使用ps H -eo pid,tid,%cpu | grep 进程id 命令查看进程中的线程状态

3、找到关键线程,然后使用jstack命令去查看进程中哪个线程出了问题,然后找到对应代码并修改

四、Mysql数据库

1、InnoDB和MyISAM的区别和使用场景

事务:InnoDB支持事务,而MyISAM不支持

外键:InnoDB支持外键,而MyISAM不支持

锁:InnoDB支持行级锁(并发量高)和表锁,MyISAM只支持表锁

索引:InnoDB不支持全文索引,MyISAM支持全文索引(全文索引是基于分词的索引,能更快速地在文本中检索一段信息内容)

使用场景:

InnoDB适合需要高并发或事务的场景,

MyISAM适合读操作远远大于写操作且不需要事务的场景 

属性InnoDBMyISAM
事务支持事务不支持事务
外键支持外键不支持外键
支持行锁(并发更高)和表锁只支持表锁(不会死锁)
索引不支持全文索引支持全文索引(查询更快)
适用场景并发量高或者需要事务读远多于写且不需要事务
2、如何防止sql注入?

在编写sql语句时,使用参数传递的方式去给变量赋值而不是直接嵌入到sql语句中

在mybatis的mapper中,我们可以用#{value}的方式去传递参数防止sql注入。

3、怎样实现幂等

幂等:也就是相同条件下对一个业务的操作,不管操作多少次,结果都是一样

实现方案:唯一索引(通常是主键),乐观锁(通常是version字段),token+redis(令牌-缓存)

token:token是服务端生成的一串字符串,当客户端登录后,服务端将token作为令牌发送给客户端,之后客户端访问服务端时,只需要校验token即可,不需要再验证用户和密码,redis作为缓存保存令牌到服务端。

4、一条sql语句的执行流程

Mysql分为server层和存储引擎层

  1. 建立连接:server层的连接器验证客户端发来的用户名和密码是否正确,如果错误就返回错误提示,如果正确就校验用户的权限
  2. 查询缓存:当执行sql查询语句时,mysql会先去缓存中查询是否有记录,如果有记录就直接返回,没有的话就去数据库中查询,然后将查询记录记录到缓存中
  3. 分析器:分析器的作用是对sql语句进行词法分析和语法分析,词法分析就是对sql语句中的关键词进行分析,比如查询到select关键词就知道这是一条查询语句,此外还会分析被操作的表,条件语句的字段等,语法分析就是判断这条语法是否正确。
  4. 优化器:对sql语句进行优化
  5. 执行器:调用存储引擎的接口,执行sql语句,执行之前会判断一下有没有权限
 5、Mysql的事务特性(ACID)
  • 原子性:单个事务是不可分割的最小工作单元,事务中的所有操作,要么全都执行成功,要么全都执行失败。通过undolog日志解决
  • 一致性:数据库总是从一个一致性的状态转换到另外一个一致性的状态。如果事务成功完成,那么所有变化都将成功应用,如果失败,就会回滚到原始状态。通过undolog日志解决
  • 持久性:一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。通过redolog日志解决
  • 隔离性:事务与事务之间互不影响。通过加锁实现
6、 Mysql事务的隔离级别,脏读、不可重复度,幻读。

脏读:事务读取到了未提交的数据,比如A查询到了B修改了但未提交的数据

不可重复读:事务对同一内容的两次查询结果不一致,比如A查询到数据不存在,B插入并提交,A再次查询时发现存在

幻读:事务在插入前检查到数据不存在,插入时却发现数据已经存在无法插入,比如A发现数据不存在准备插入,但B此时插入数据并提交

不可重复读重点在于update,而幻读的重点在于insert或delete。

Mysql事务的隔离级别从低到高为:

  1. 读未提交RC:所有的事务都可以读取其他事务未提交的结果,会发生脏读,不可重复读,幻读
  2. 读已提交RU:事务开始时,只能读取到已提交的修改,会发生不可重复读,幻读
  3. 可重复读RR:事务开始时,在同一条件的查询返回的内容是一致的,会发生幻读,mysql默认隔离级别
  4. 可串行化:所有事务依次执行,事务之间不能互相干扰,能防止脏读,不可重复读,幻读
7、 锁的类型,行锁和表锁,共享锁和独占锁
锁的类型是否会死锁并发量适用场景
表锁不会死锁并发量低

1、读多写少(上一次锁连续读)

2、写特别多(如果用行锁,会导致其他事务长时间锁等待,锁冲突频率变高)

行锁会死锁并发量高并发量高的场合
页面锁会死锁并发量中

 行锁:行锁就是加在单行上的锁,只存在于InnoDB引擎中,分为共享锁(读锁)和独占锁(写锁)

类型作用
共享锁(读锁)获得共享锁的事务被允许读一行,并阻止其他事务获得该行的独占锁
独占锁(写锁)获得独占锁的事务被允许写一行,并阻止其他事务获得该行的共享锁和独占锁

读锁和写锁的

  • 共同点: 自动加锁
  • 不同点:共享锁可能会死锁,独占锁不会 / 读锁读取数据后释放,写锁要事务结束后才释放
  • 语法:SELECT...+LOCK IN SHARE MODE / INSERT...+FOR UPDATE

 此外还有意向共享锁和意向独占锁,这是InnoDB自动添加的,事务在获取读锁和写锁前必须先获取该表的对应意向锁。

表锁:表锁是加在整个表的锁

表锁也有两种锁,共享锁和独占锁,作用与行锁的基本相同

语法:LOCK TABLE {TABLENAME} READ / LOCK TABLE {TABLENAME} WRITE

8、内关联,全关联,左关联,右关联的区别

内关联(INNER JOIN):所有的查询结果在关联的两张表中都有记录

全关联:联结了那些在相关表中没有关联的行,分为左关联和右关联

左关联(LEFT JOIN):从From子句的左边表中选择所有行,可以没有右边的表的行

左关联(RIGHT JOIN):从From子句的右边表中选择所有行,可以没有左边的表的行

9、索引,索引的分类

索引的作用相当于是图书的目录,它是对数据表中一列或多列值进行排序的一种存储结构

索引的分类:普通索引,主键索引,复合索引,唯一索引,外键索引,全文索引

普通索引:

        mysql表中最基本的索引

        语法:CREAT INDEX index_name ON TABLE table_name(column)

主键索引:

        在Mysql创建表指定主键的时候,会自动在主键上建立一个主键索引,标志着主键具有唯一性且不为空 

复合索引:

        复合索引也叫组合索引,在建立索引的时候使用表中的多个字段,就比如说使用身份证号和手机号建立索引

        语法:CREAT INDEX index_name ON TABLE table_name(column1,column2)

 唯一索引:

        唯一索引标志着该字段具有唯一性

        语法:CREAT UNIQUE INDEX index_name ON TABLE table_name(column)

如果创建表的时候加索引语法为:

CREATE TABLE tablename(
    propname1 type1,
    ……
    propnamen type..n,
    UNIQUE INDEX|KEY index_name (column [(length)] [ ASC | DESC ] ) );

   //简化为:UNIQUE INDEX index_name (column);

10、索引的优缺点和使用规则

索引的优点主要体现在:

  1. 提高了用户查询的速度,提高了性能
  2. 降低了查询中分组和排序的时间
  3. 加速表与表之间的连接
  4. 通过索引的唯一性,可以确保表中数据的唯一性

索引的缺点主要体现在:

  1. 空间:索引占用了磁盘空间
  2. 时间:当数据量较大时,索引的创建和维护也是非常耗费时间的
  3. 每次对数据进行增删改查时,索引也要进行动态维护,降低了数据的维护速度

总结,以下条件不适合添加索引:

  1. 在查询中很少使用的列
  2. 数据值很少的列
  3. 写操作远多于读操作的列(修改频率大于查询频率的列)

使用索引的规则:

  1. 对查询频率高的字段创建索引
  2. 对经常需要分组、排序和联合操作的字段创建索引
  3. 尽量使用唯一索引
  4. 多使用短索引
  5. 索引的数目不要不多(不然会降低增删改的效率)
11、索引的原理,B+树的优点

首先我们了解一下B树(Balance Tree),即平衡树的意思,下图即是一颗B树

图片

 B树的每一个节点代表一个磁盘块,每一个节点上面存储了多个键值对和指针

B树相对于平衡二叉树,每个节点存储了更多的键(Key)和数据(Data),并且每个节点拥有更多的子节点,子节点的个数就是B树的阶数,比如上图就是的B树就是三阶B树

基于这个性质,B树查询数据读取磁盘的次数会减少,查找的效率会比平衡二叉树高。

B+树是对B树的进一步优化,下图是一颗B+树

 在B+树中,非叶子节点不会存储数据(Data),只会存储键值(Key),而所有的数据都存储在叶子节点上,并且所有数据都是按照顺序排列的

在数据库中,页(节点)的大小是固定的,InnoDB默认为16kb,页如果不存储数据,就可以存储更多的键值,树的阶数会提升,

这样一来我们查询数据访问磁盘的次数会进一步减少,查询的效率更高,根节点默认存储在内存,所以我们访问一次三阶B+树索引的数据只需要2次磁盘IO

因为B+树索引中所有的数据都存储在叶子节点上,并且按顺序排列,所以对数据的范围查找,分组,排序都会更加简单

MyISAM中的B+树索引实现与innodb中的略有不同。在MyISAM中,B+树索引的叶子节点并不存储数据,而是存储数据的文件地址。

B+树相对于B树的特点:

  1. 只有叶子节点才会存放数据,非叶子节点只存放索引
  2. B+树中每个节点都通过双向链表连接,叶子节点中的数据通过单向链表连接
  3. 非叶子节点中的索引会出现在子节点中,而且是子节点中索引的最大(最小)值
  4. 非叶子节点的索引树=子节点数

B+树相对于B树的优点:

  1.  普通查询,范围查询,分组查询,排序的速度更快
  2. 插入和删除的速度更
Java 软件工程师面试资料大整合 1 Java 面霸 1 1. int 和 Integer 有什么区别? 8 2. String 和StringBuffer的区别 8 3. 运行时异常与一般异常有何异同? 8 4. 说出ArrayList,Vector,LinkedList的存储性能和特性 8 5. EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。 9 6. Collection 和 Collections的区别。 9 7. &和&&的区别。 9 8. HashMap和Hashtable的区别。 10 9. final, finally, finalize的区别。 10 10. sleep() 和 wait() 有什么区别? 10 11. Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型? 10 12. error和exception有什么区别? 11 13. 同步和异步有何异同,在什么情况下分别使用他们?举例说明。 11 14. 简述synchronized和java.util.concurrent.locks.Lock的异同 ? 11 15. 当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 11 16. abstract class和interface有什么区别? 12 17. abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized? 12 18. 接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? 12 19. heap和stack有什么区别。 13 20. forward 和redirect的区别 13 21. EJB与JAVA BEAN的区别? 13 22. Static Nested Class 和 Inner Class的不同。 13 23. JSP中动态INCLUDE与静态INCLUDE的区别? 14 24. List, Set, Map区别 14 25. 集合类都有哪些?主要方法? 14 26. 简述逻辑操作(&,|,^)与条件操作(&&,||)的区别。 14 27. XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 14 28. JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么? 15 29. Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)? 15 30. 构造器Constructor是否可被override 15 31. try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后? 16 32. 应用服务器与WEB SERVER的区别? 16 33. BS与CS的联系与区别。 16 34. 启动一个线程是用run()还是start()? 17 35. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 18 36. swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上? 18 37. 一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 18 38. 比较truncate和delete 命令 18 39. 解释$ORACLE_HOME 和$ORACLE_BASE的区别? 19 40. session与cookie的区别和联系? 19 41. Statement和PrepareStatement的区别 19 42. JSP的内置对象及方法。 19 43. JSP的常用指令 20 44. 四种会话跟踪技术 20 45. Request对象的主要方法: 21 46. jsp有哪些动作?作用分别是什么? 21 47. 两种跳转方式分别是什么?有什么区别? 22 48. get和post的区别? 22 49. JDK,JRE,JVM的区别? 22 50. Java中常见类,方法,接口 23 51. 多线程 23 51.1. 线程的基本概念 23 51.2. Java中的线程有四种状态 23 51.3. 多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么? 24 51.4. 线程同步的方法。 24 51.5. java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 25 52. 数据连接池 25 52.1. 连接池的基本原理: 25 52.2. 连接池的工作机制 25 52.3. 建立连接池 26 52.4. 连接池内连接的使用与释放 26 52.5. 配置连接池 26 52.6. 配置tomcat 6.0.10连接池 26 52.7. Hibernate实现数据库的连接不同方式: 28 52.8. 有几种开源的数据库连接池: 29 53. 描述一下JVM加载class文件的原理机制? 30 54. socket编程 30 54.1. 什么是TCP/IP、UDP? 30 54.2. Socket在哪里呢? 31 54.3. Socket是什么呢? 32 54.4. socket的实现步骤 37 55. Servlet 38 55.1. Servlet工作流程 38 55.2. servlet的生命周期 38 55.3. Servlet执行时一般实现哪几个方法? 38 56. 会话跟踪 39 56.1. Cookie 39 56.2. session 39 56.2.1. Session 生命周期 39 57. EJB的几种类型 39 58. 排序都有哪几种方法?请列举。用JAVA实现一个快速排序。 40 59. 请对以下在J2EE中常用的名词进行解释(或简单描述) 40 59.1. web 容器 40 59.2. EJB容器 40 59.3. JNDI 40 59.4. JMS 41 59.5. JTA 41 59.6. JAF 41 59.7. RMI/IIOP 41 60. JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗? 41 61. MVC的各个部分都有那些技术来实现?如何实现? 42 62. java中实现多态的机制是什么? 42 63. 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收? 42 63.1. 判断该对象是否是时候可以收集方法 43 63.1.1. 引用计数 43 63.1.2. 对象引用遍历 43 63.2. 几种垃圾回收机制 43 63.2.1. 标记-清除收集器 43 63.2.2. 标记-压缩收集器 43 63.2.3. 复制收集器 44 63.2.4. 增量收集器 44 63.2.5. 分代收集器 44 63.2.6. 并发收集器 44 63.2.7. 并行收集器 44 63.3. Sun HotSpot 1.4.1 JVM堆大小的调整 44 63.4. BEA JRockit JVM的使用 45 63.4.1. Bea JRockit JVM支持4种垃圾收集器: 46 63.5. 如何从JVM中获取信息来进行调整 46 63.6. Pdm系统JVM调整 47 63.6.1. 服务器:前提内存1G 单CPU 47 63.6.2. 客户机:通过在JNLP文件中设置参数来调整客户端JVM 47 64. 什么时候用assert。 47 65. 什么是java序列化,如何实现java序列化? 48 65.1. java序列化、反序列化 48 65.2. 对象的序列化主要有两种用途: 48 65.3. 对象序列化包括如下步骤: 49 65.4. 对象反序列化的步骤如下: 49 66. 反射机制 49 66.1.1. 传统的类型转换。 49 66.1.2. 通过Class对象来获取对象的类型。 49 66.1.3. 通过关键字instanceof或Class.isInstance()方法 49 67. 说出一些常用的类,包,接口,请各举5个 50 68. XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 51 69. jdbc 51 69.1. 简述 51 69.2. JDBC调用数据库的基本步骤 52 69.3. JDBC URL 52 70. MVC设计模式 53 71. Hibernate 54 71.1. Hibernate 介绍 54 71.2. Hibernate 实现原理 55 71.3. Hibernate 优点 56 71.4. Hibernate 的缓存体系 56 71.4.1. 一级缓存: 56 71.4.2. 二级缓存: 56 71.4.3. 缓存管理 56 71.5. Hibernate 中Java对象的状态 58 71.5.1. 临时状态 (transient) 58 71.5.2. 持久化状态(persisted) 58 71.5.3. 游离状态(detached) 58 71.5.4. hibernate的三种状态之间如何转换 59 71.6. Hibernate并发机制,并发问题的处理。 59 71.6.1. Hibernate并发机制 59 71.6.2. 并发问题解决方案 59 71.7. Hibernate是如何延迟加载? 60 71.8. Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系) 60 71.9. 说下Hibernate的缓存机制 60 71.10. Hibernate的查询方式 60 71.11. 如何优化Hibernate? 61 71.12. Hibernate和spring中常出现的几个异常 61 71.13. Hibernate与jdbc的联系 62 71.14. Hibernate与Spring的联系 62 71.15. Hibernate自带的分页机制是什么?如果不使用Hibernate自带的分页,则采用什么方式分页? 62 71.16. hibernate中一对多配置文件返回的是什么? 63 71.17. hibernate拒绝连接、服务器崩溃的原因?最少写5个 63 71.18. Hibernate主键介绍 63 71.18.1. Assigned 63 71.18.2. Hilo 63 71.18.3. Increment 64 71.18.4. Identity 64 71.18.5. Sequence 64 71.18.6. Native 64 71.18.7. UUID 64 71.18.8. Foreign GUID 65 71.19. Hibernate源码中几个包的作用简要介绍 65 72. struts 66 72.1. struts 简介 66 72.2. STRUTS的应用(如STRUTS架构) 66 72.3. 请写出Struts的工作原理、工作机制 67 72.4. struts的处理流程。 67 72.5. Struts 2框架的大致处理流程如下: 68 72.6. Struts体系结构中的组件 69 72.7. struts如何实现国际化 70 72.8. struts2.0的常用标签 71 72.9. action是单实例还是多实例,为什么? 73 72.10. Struts的validate框架是如何验证的? 74 72.11. dispatchAction是用什么技术实现的? 74 72.12. struts2.0的mvc模式?与struts1.0的区别? 74 72.13. struts1.2和struts2.0的区别?如何控制两种框架中的单例模式? 74 73. Spring 75 73.1. Spring 简介 75 73.2. 为什么要用Spring? 76 73.3. spring工作机制或工作原理 76 73.4. Spring是什么?根据你的理解详细谈谈你的见解。 76 73.5. 项目中如何体现Spring中的切面编程,具体说明。 77 73.6. 项目中用到的Spring中的切面编程最多的地方:声明式事务管理。 77 73.7. spring的事务如何配置 77 73.8. transaction有那几种实现(事务处理)(Spring) 79 73.9. Spring IoC 79 73.10. Spring AOP面向方面编程 82 74. 项目中为什么使用SSH 85 75. Spring在SSH中的作用 86 76. weblogic 86 76.1. 如何给weblogic指定大小的内存? 86 76.2. 如何设定的weblogic的热启动模式(开发模式)与产品发布模式? 86 76.3. 如何启动时不需输入用户名与密码? 86 76.4. 在weblogic管理制台中对一个应用域(或者说是一个网站,Domain)进行jms及ejb或连接池等相关信息进行配置后,实际保存在什么文件中? 86 76.5. 在weblogic中发布ejb需涉及到哪些配置文件 87 76.6. 如何在weblogic中进行ssl配置与客户端的认证配置或说说j2ee(标准)进行ssl的配置 87 76.7. 如何查看在weblogic中已经发布的EJB? 87 76.8. 说说在weblogic中开发消息Bean时的persistent与non-persisten的差别 87 77. tomcat 87 77.1. 解决端口冲突导致tomcat无法启动的问题 87 77.2. 修改java虚拟机内存 88 77.3. 修改tomcat连接数 88 77.4. 禁止列出目录下的文件 88 77.5. 设置session失效的时间 89 77.6. 设置MIME响应类型 89 77.7. 设置tomcat的默认访问页面 89 77.8. 设置tomcat管理用户 89 77.9. 附录 90 78. websphere 90 79. 常见异常 90 79.1. nullpointerexception 90 79.2. classnotfoundexception 90 79.3. arithmeticexception 90 79.4. arrayindexoutofboundsexception 91 79.5. illegalargumentexception 91 79.6. illegalaccessexception 91 80. 异常机制 97 81. 异常的分类 97 82. 异常的使用方法 98 83. JAVA代码查错 101 83.1. 判断 101 83.2. 判断 102 83.3. 判断 102 83.4. 判断 102 83.5. 判断 102 83.6. 判断 103 83.7. 判断 103 83.8. 判断 103 83.9. 判断 104 83.10. 判断 104 83.11. 判断 105 84. 编码 106 84.1. 写出一个单例模式 106 84.2. 我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? 106 84.3. 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。以下程序使用内部类实现线程,对j增减的时候没有考虑顺序问题。 107 84.4. 现在输入n个数字,以逗号”,”分开;然后可选择升或者降序排序;按提交键就在另一页面显示按什么排序,结果为,提供reset 108 84.5. 金额转换,阿拉伯数字的金额转换成中国传统的形式如:(¥1011)->(一千零一拾一元整)输出。 109 84.6. 内部类的实现方式? 112 84.7. 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+汉的半个”。 113 84.8. 将一个键盘输入的数字转化成中文输出(例如:输入1234567,输出:一百二拾三万四千五百六拾七),请用java语言编一段程序实现! 114 84.9. 题目1:用1、2、2、3、4、5这六个数字,用java写一个main函数,打印出所有不同的排列,如:512234、412345等,要求:"4"不能在第三位,"3"与"5"不能相连. 117 84.10. 写一个方法,实现字符串的反转,如:输入abc,输出cba 119 84.11. 请用java写二叉树算法,实现添加数据形成二叉树功能,并以先序的方式打印出来. 119 84.12. 请写一个java程序实现线程连接池功能? 122 84.13. 编一段代码,实现在控制台输入一组数字后,排序后在控制台输出; 122 84.14. 列出某文件夹下的所有文件; 123 84.15. java调用系统命令实现删除文件的操作; 123 84.16. java实现从文件中一次读出一个字符的操作; 124 84.17. 列出一些控制流程的方法; 124 84.18. 编写了一个服务器端的程序实现在客户端输入字符然后在控制台上显示,直到输入"END"为止,让你写出客户端的程序; 124 84.19. 用jdom解析xml文件时如何解决中文问题?如何解析? 127 84.20. Jquery ajax 实现异步 129
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值