2021年java重点面试题

这里写自定义目录标题

1. ==与equals的的区别

==作用:

  • 基本类型:比较值是否相等
  • 包装对象:两个同类型的包装对象比较地址是否相等。一个基本类型和对应包装类型比较值是否相等。
  • 引用类型:比较内存地址是否相等
  • 不能比较没有 父子关系的两个对象

equals 作用;

  • 如果没有重写equals方法,那么默认使用this=obj比较
  • 如果重写了equals方法,那么比较对象的值是否相等,一般equals与hashcode一起被重写,因为有些情况下 两个对象的值不相等的情况下hashcode值是相等,但真实两个对象的值实不相等的。但调用equals返回true的时候hashcode一定是相等的

2. finalized的作用

finalize()方法是Object类中提供的一个方法,在GC准备释放对象所占用的内存空间之前,它将首先调用finalize()方法 。
finalize()方法中一般用于释放非Java 资源(如打开的文件资源、数据库连接等)

3. finally语句块一定执行吗?

很多特殊情况下不一定是

  • 在执行到try-catch-finally之前程序返回
  • 在执行到try=catch-finally之前程序抛出异常
  • 系统退出

4.String不属于基础的数据类型

-基本类型:byte1,short2,char2,int4,long8,float4,double8,boolean1

5. 如何将字符串反转

  • 采用Stringbuilder或StringBuffer的reverse方法
  • 转换成字符数组,遍历反转数组
  • 采用递归

6.String类的常用方法

  • equals:字符串是否相同
  • equalsIgnoreCase:忽略大小写后字符串是否相同
  • compareTo:根据字符串中每个字符的Unicode编码进行比较
  • compareToIgnoreCase:根据字符串中每个字符的Unicode编码进行忽略大小写比较
  • indexOf:目标字符或字符串在源字符串中位置下标
  • lastIndexOf:目标字符或字符串在源字符串中最后一次出现的位置下标
  • valueOf:其他类型转字符串
  • charAt:获取指定下标位置的字符
  • codePointAt:指定下标的字符的Unicode编码
  • concat:追加字符串到当前字符串
  • isEmpty:字符串长度是否为0
  • contains:是否包含目标字符串
  • startsWith:是否以目标字符串开头
  • endsWith:是否以目标字符串结束
  • format:格式化字符串
  • getBytes:获取字符串的字节数组
  • getChars:获取字符串的指定长度字符数组
  • toCharArray:获取字符串的字符数组
  • join:以某字符串,连接某字符串数组
  • length:字符串字符数
  • matches:字符串是否匹配正则表达式
  • replace:字符串替换
  • replaceAll:带正则字符串替换
  • replaceFirst:替换第一个出现的目标字符串
  • split:以某正则表达式分割字符串
  • substring:截取字符串
  • toLowerCase:字符串转小写
  • toUpperCase:字符串转大写
  • trim:去字符串首尾空格

7.抽象类的注意事项

  • 不能实例化
  • 可以有抽象方法,无需实现
  • 抽象方法不能声明为静态
  • 抽象方法不能用private修饰
  • 抽象方法不能用final修饰:代表不可修改、不可继承的。

8.接口与抽象类的区别

  • 抽象类可以有构造方法,接口不能有
  • 抽象类可以拥有非抽象普通方法,接口所有方法都是抽象的,并且抽象方法类型都是public abstract 的,JDK1.8之后接口中的抽象方法可以有default和static
  • 接口是一种更为严格的抽象类,是一种规范

9.Java访问权限修饰符

在这里插入图片描述

10.Java中<< >> >>>的意思

  • <<表示左移:16 << 1–>10000=16 -->100000=32
  • 表示右移:16 >> 1–>10000=16 -->1000=8

  • 表示无符号右移

11.throw和throws的区别

  • throw:throw是程序员在方法内自信抛出异常,如果不是运行时异常,就需要将异常抛出给该方法的调用者(throws)或者进行捕获处理try-catch
  • throws:用于声明该方法可能抛出异常(可能多个),不能单独使用

12.try-catch-finally执行顺序

JAVA中try、catch、finally带return的执行顺序总结
如果try、catch中有return语句,那么会先去执行finally代码块,执行完成之后采取执行try、catch中的return语句(return语句中保存的信息是在try中保存留下来的,finally里面修改不会影响)

13.常见的异常类

  • Throwable是异常根类
    • Error
      • IOError
      • LinkageError
      • ReflectionError
      • ThreadDeath
      • VirtualMachineError
    • Exception
      • SQLException
      • CloneNotSupportedException
      • DataFormatException
      • InterruptedException
      • IOException
      • ReflectiveOperationException
      • RuntimeException
        • ArithmeticExceptio
        • ClassCastException
        • ConcurrentModificationException
        • IllegalArgumentException
        • IndexOutOfBoundsException
        • NoSuchElementException
        • NullPointerException
        • SecurityException

14.Java内部类种类

  • 成员内部类
  • 方法内部类
  • 匿名内部类
    • 继承式匿名内部类
    • 接口式匿名内部类
    • 参数式匿名内部类
    • 静态嵌套类
  • 内部类的作用:
    • 封装性:将该类的访问做出一定限制
    • 多继承:利用接口实现Java类的多继承效果
    • 解决继承以及实现接口出现同名问题

15.反射是什么?能做什么?

  • 反射的概念
    在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法。
  • 反射的功能
    • 获取任意类的名称、package 信息、所有属性、方法、注解、类型、类加载器、modifiers(public、static)、父类、现实接口等
    • 获取任意对象的属性,并且能改变对象的属性
    • 调用任意对象的方法
    • 判断任意一个对象所属的类
    • 实例化任意一个类的对象

16.动态代理是什么?应用场景?

  • 动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法
  • 实现方式
    • JDK中的动态代理
    • Java类库CGLib
  • 应用场景
    • 统计每个API的请求耗时
    • 统一的日志输出
    • 校验被调用的API是否已经登录和权限鉴定
    • Spring的AOP功能模块就是采用动态代理的机制来实现切面编程

17.如何实现动态代理

  • JDK动态代理
  • CGLib动态代理
  • 使用Spring aop模块完成动态代理功能

18.Java序列化及注意事项

  • 序列化:将Java对象转换成字节流的过程
  • 反序列化:将字节流转换成Java对象的过程
  • 注意事项
    • 父类可序列化,则子类也可以序列化
    • 对象中的某个属性时对象类型,则也要实现Serializale
    • 声明为static个transient的成员变量不能序列化,static成员变量是描述类级别的,而transient表示是临时数据

19.什么场景要对象克隆

  • 方法需要return引用类型对象,但不想修改对象
  • 方法参数需要引用类型对象作为参数,但不想修改对象

20.引用拷贝、深拷贝和浅拷贝的区别?

-引用拷贝:相当于
Teacher teacher = new Teacher(“riemann”, 28);
Teacher otherTeacher = teacher;

  • 浅拷贝:复制基本类型的属性;引用类型的属性复制,复制栈中的变量和变量指向堆内存有的对象的指针,不复制堆内存中的对象。两个引用类型指向同一个堆内存的对象
  • 深拷贝:复制基本类型的属性;引用类型的属性复制:复制栈中的变量和变量指向堆内存中的对象的指针和堆内存中的对象

21.如何实现对象克隆与深拷贝

  • 克隆:事项Cloneable接口,重写clone()方法
  • 不实现Cloneable接口,汇报CloneNOSupportedException
  • Object的clone()方法是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象
    - 对象的属性的Class也实现Cloneable接口

22.Java跨平台运行的原理

  • 源文件编译成Class文件通过虚拟机解释成机器码运行
  • .class文件是面向虚拟机,不面向任何操作系统
  • 不同平台的虚拟机是不同的,但它们给出了相同的接口
  • Java的跨平台依赖不同系统的Java虚拟机

23.Java的安全性体现在哪?

  • 引用取代了指针,指针的功能强大
  • 拥有一个异常处理机制,使用关键字throw、throws、try、catch、finally
  • 强制类型转换需要一定的规则- 运行环境提供保障机制:字节码校验器-类装载器-运行时内存布局-文件访问限制
  • JVM垃圾回收机制

24.Java的版本以及对应的应用场景

  • J2SE:标准版,包含Java语言的核心类
  • J2EE:企业版,包含J2SE中的类和企业级开发的类。比如web相关的servlet,jsp,xml等相关类
  • J2ME:微型版,包含J2SE中部分类,新添加一个专有类,一把用于嵌入式开发,如手机,机顶盒等

25.什么是JVM?

  • .class文件在JVM上运行,JVM会解释给操作系统执行
  • 有自己的指令集,解释自己的指令到CPU指令集和系统资源的调用

26.JDK的文件目录

  • bin:各种命令工具, java 源码的编译器 javac、监控工具 jconsole、分析工具 jvisualvm 等
  • include:与 JVM 交互C语言用的头文件
  • lib:类库
  • jre:Java 运行环境
  • db:安装 Java DB 的路径

27.什么是JRE?

  • Java运行环境的缩写,包含JVM标准及Java核心类库
  • 是Java程序的运行环境,不是开发环境

28.JDK、JRE、JVM之间的关系

  • JDK是Java程序开发的工具包,包含JRE
  • JDK、JRE均包含JVM
  • JVM包含Java应用程序的类的解释器和类加载器

29.i++和++i的区别

  • i++: 先在i所在的表达式中使用i的当前值,后让i加1
  • ++i :让i先加1,然后在i所在的表达式中使用i的新值
  • int a = 3;int b = a++ -->b =3

30.&和&&的作用和区别

  • &:逻辑与,& 两边的表达式都会进行运算
  • &&:&& 左边的表达式结果为 false 时,&& 右边的表达式不参与计算

31.如何让计算机最高效算出2*8

  • CPU直接支持位运算,效率最高
  • 答案:2 << 3 -->10=0–>10000=15

32.Java中基本类型转换规则

  • 等级高低
    • byte、short、int、long、float、double
    • char、int、long、float、double
  • 自动转换:低级自动向高级转换
  • 强制转换:高级向低级必须强制转换,注意会损伤精度

33.谈谈对面向过程和面向对象的理解

  • 两者相辅相成,宏观上面向对象把握复杂事务的关系,微观上面向过程去处理
  • 面向过程以实现功能函数开发为主,面向对象以封装类为主
  • 面向对象拥有继承以及多态,面向过程没有

34.重载和重写的区别

  • 作用范围:重写是父子类之间,重载在一个类里面
  • 参数类型:重写必须一致,重载必须相同
  • 返回类型:重写返回相同类型或其子类,重载可修改
  • 抛出异常:重写可减少或删除,不能抛出新的或者更广的异常,重载可修改
  • 访问权限:重写一定不能做更严格的限制,重载可修改

35.java.lang.Object的常用方法

  • public final native Class<?> getClass(); 获取类结构信息
  • public native int hashCode() 获取哈希码
  • public boolean equals(Object) 默认比较对象的地址值是否相等,子类可以重写比较规则
  • protected native Object clone() throws
  • CloneNotSupportedException 用于对象克隆
  • public String toString() 把对象转变成字符串
  • public final native void notify() 多线程中唤醒功能
  • public final native void notifyAll() 多线程中唤醒所有等待线程的功能
  • public final void wait() throws InterruptedException 让持有对象锁的线程进入等待
  • public final native void wait(long timeout) throws
  • InterruptedException 让持有对象锁的线程进入等待,设置超时毫秒数时间
  • public final void wait(long timeout, int nanos) throws
  • InterruptedException 让持有对象锁的线程进入等待,设置超时纳秒数时间
  • protected void finalize() throws Throwable 垃圾回收前执行的方法

35.多态的实现

  • 多态的概念:
    • 同一个接口,使用不同的实例而执行不同的操作。同一个行为具有多个不同表现形式
  • 实现多态的条件
    • 存在继承关系或者基于接口实现
    • 子类重写父类方法
    • 父类引用指向子类对象
  • 多态的好处
    • 消除类型之间的耦合关系
    • 可替换性
    • 可扩充性
    • 灵活性
    • 简化性

36.什么是Java的垃圾回收机制

  • 概念
    • 程序员无需控制内存回收,JVM会跟踪程序中有用的对象,确认哪些是无用的,并会自动回收无用对象
  • 特点
    • 回收JVM堆内存中的对象空间,不负责栈内存数据
    • 无法释放一些操作资源的释放,如数据库连接,输入流输出流,Socket连接等。
    • 当对象的引用变量设置为null,垃圾回收机制可以在下次执行时回收该对象
    • 垃圾回收机制在回收任何对象之前,会先调用对象的finalize()方法
    • 可以通过System.gc()或Runtime.getRuntime().gc()通知系统进行垃圾回收,但系统是否回收不确定
    • 不要主动调用对象的finalize()方法,垃圾回收机制调用

37.java.sql.Date和java.util.Date的区别

  • java.sql.Date是java.util.Date的子类
  • java.util.Date是JDK中的日期类,精确到时分秒毫分
  • java.sql.Date是数据库的Date类型,只有年月日部分,其他时分秒等都是为0

38.内存泄露和内存溢出的区别

  • 内存溢出(Out OF Memory):指程序在申请内存时,没有足够的内存空间供其使用
  • 内存泄露(Memory Stack):指程序在申请内存后,无法释放已申请的内存空间,内存泄露堆积会导致内存被占光

39.不通过构造方法能创建对象吗?。。。。。。

  • java创建对象的方式
    • 调用构造方法
      • 用new语句创建对象
      • 运用反射,调用java.lang.Class或java.lang.reflect.Constructor类的newInstance()方法
    • 不调用构造方法
      • 调用对象的clone()方法
      • 运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法

40.同步代码块和同步方法的区别。。。

  • 同步方法就是在方法前加关键字synchronized
  • 同步代码块则是在方法内部使用synchronized
  • 加锁对象相同的话,同步方法锁的范围大于等于同步代码块
  • 同步方法如果是static方法,等同于同步方法快加锁在改CLass对象上

41.静态内部类和非静态内部类的区别。。。

  • 静态内部类不需要外部类的引用;非静态内部类需要持有外部类的引用
  • 静态内部类可以拥有静态方法,属性;非静态内部类而则不能拥有静态方法、属性
  • 静态内部类只能访问外部类的静态成员,非静态内部类任意
  • 静态内部类不依赖外部类的实例,直接实例化内部类对象。

42.存在 i + 1 < i 的数吗?为什么?。。。。。。

存在 i + 1 < i 的数吗?为什么

43.接口可以继承接口、抽象类可以实现接口、抽象类可以继承实体类。

44.可序列化对象为什么要定义serialversionUID值?。。。。。。

  • serialversionUID是为了序列化对象版本控制,告诉JVM各版本序列化时是否兼容
  • 如果新版本这个值被修改,那么就不兼容
  • 仅增加一个属性,希望向下兼容,老版本的数据都保留,那么这个值就不用修改
  • 删除了一个属性或者更换了类的继承关系,那么不兼容,这时候应该手动更新serialversionUID。

45.Class类的getDeclaredFields()与getFields()方法的区别?。。。。。。

  • getDeclaredFields(): 获取所有本类自己声明的属性, 不能获取父类和实现的接口中的属性
  • getFields(): 只能获取所有 public 声明的属性, 包括获取父类和实现的接口中的属性

46.final修饰变量,是引用不能变?还是引用的对象不能变?

  • final对对象的作用:防止对象的变量值(属性值)改变
  • final修饰基本数据类型变量,是值不能变
  • final修饰引用类型变量,栈内存中的引用不能改变,所指向的堆内存中的变量的属性值可以改变

47.Java的转义字符表。。。。。。

转移字符表

48.Java正则表达式。。。。。。

在这里插入图片描述

49.基本类型byte表示的数值范围是多少?

  • -128-127

50.switch语句的注意事项

  • 在switch(a)中,a会匹配相对应的case,找到之后便执行相应语句,如果没有break,那么继续往下执行直至break。

51.静态与非静态成员变量的区别

  • 生命周期不同:非静态成员变量随着对象的创建而存在的;静态成员变量随着类的加载而存在
  • 调用方式不同
  • 数据存储位置不同:成员变量数据存储在堆内存的对象中,对象的特有数据。静态变量数据存储在方法区(共享数据区)的静态区,JDK1.7静态变量存储到堆内存中

52.类型转换实例

- short s1 = 1; s1 = s1 + 1;

编译报错,s1 + 1自动升级为 int 型,int 型赋值给 s1,需要手动强转

- short s1 = 1; s1 += 1;

隐含类型强转,不会报错

53.switch能否作用在byte、long、String上?

  • 早期 JDK,switch(expr),expr 可以是 byte、short、char、int
  • JDK 1.5 开始,引入了枚举(enum),expr 也可以是枚举
  • JDK 1.7 开始,expr 还可以是字符串(String) 长整型(long)是不可以的

54.abstract方法是否可以是static的?native?synchroized?

  • 都不能
    • 抽象方法需要子类重写,而静态方法无法重写
    • 本地方法是调用本地方法库,而抽象方法是没有实现的
    • 抽象方法没有方法体,而synchronize需要在方法体中

55.内部类访问外部类的成员限制

  • 内部类可以访问外部类的成员变量和包括私有
  • 内部类想要访问外部类的额局部变量,那么该局部变量必须使用final修饰

55.静态代码块的执行优先级比构造器的优先级高

56.反射主要实现类有哪些?。。。。。。。

  • java.lang.reflect 包中
    • java.lang.Class :一个类
    • java.lang.reflect.Field :类的成员变量(属性)
    • java.lang.reflect.Method :类的成员方法
    • java.lang.reflect.Constructor :类的构造方法
    • java.lang.reflect.Array :提供了静态方法动态创建数组,访问数组的元素

57.Class类的作用是什么?如何获取Class对象?

Class类是Java反射机制的起源和入口,用于获取与类相关的各种信息:类的名字、属性、方法、构造方法、父类、接口和注解等信息

  • 对象名.getClass()
  • 对象名.getSuperClass()
  • Class.forName(“oracle.jdbc.driver.OracleDriver”)
  • 类名.class

58.1.反射的使用场景、作用及优缺点?

  • 使用场景
    • 在编译时无法知道对象或者类可能属于哪些类,程序在运行时获取对象和类的信息
  • 作用
    • 通过反射使程序访问装载到JVM中类的内部信息,获取已装载类的属性信息,方法信息
  • 优点
    • 降低耦合性
    • 允许程序创建和控制任何类对象,无需编写目标类
  • 缺点
    • 性能问题:反射是一种解释操作,远慢于直接代码
    • 模糊程序内部逻辑:反射绕过了源代码,无法看到源代码中的程序逻辑,维护困难

58.2.谈谈反射在你实际开发中的使用

反射使用的不当,对性能影响比较大
反射主要用于底层的框架中,Spring就是运用了反射

  • 用IOC来注入和组装bean
  • 动态代理、面向切面、bean对象中方法的增强和替换
  • 定义的注解,也是通过反射查找的

59.面向对象设计原理有哪些?

  • 单一职责原则SRP
  • 开闭原则OCP
  • 里式替换原则LSP
  • 依赖注入原则DIP
  • 接口分离原则ISP
  • 迪米特原则LOD
  • 组合/聚合复用原则CARP

60.String类是否可以被继承

不可以,String类在JDK中广泛被使用,为了保障正确性安全性,String类使用final修饰的,不能被继承,方法不能被重写

61.String类为什么被设计为final修饰

  • String类是最常用的类之一,为了效率,禁止被继承和重写
  • 为了安全,String类中有很多调用底层的本地方法,调用操作系统的API,如果方法可以重写,可能被植入恶意代码,破坏程序,Java的安全性也体现在这里

62.String str = new String(“abc”),会创建几个String对象?

答案:一个或两个

  • 第一次调用new String(“abc”)时,会在堆内存中创建一个字符串对象,同时在字符串常量池中创建一个对象“abc”
  • 第二次调用new String(“abc”)时,只会在堆内存中创建一个字符串对象,指向字符串常量池中创建的“abc”

63.什么是assert?

assert:断言,是一种常用的调试方式,断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为 true;如果表达式计算为 false ,会报告一个 AssertionError

64.类的实例化方法调用实例

  • 类加载器实例化的操作步骤
    加载–>连接–>初始化
    • 加载父类静态变量和静态代码块
    • 加载子类静态变量和静态代码块
    • 父类非静态变量
    • 父类非静态代码块
    • 父类构造函数
    • 子类非静态变量
    • 子类非静态代码块
    • 子类构造函数

65. JVM加载class文件的原理机制

1、Java中的所有类,必须被装载到JVM才能运行,这个装载工作是由JVM中的类加载器完成的,其实类加载器本质上也是一个类,其作用便是将类文件从硬盘中读取到内存中。
Java类加载器基于三个机制实现的:委托、可见性和单一性。
(1)委托机制:是指加载一个类的请求交给父加载器,如果这个父加载器不能找到或者加载这个类,那么该加载器再加载它。
(2)可见性:子类的加载器可以看见父类加载器的加载的所有类,而父类架子爱情看不到子类加载器加载的类。
(3)单一性:一个类仅被加载一次,这是由委托机制确保子类不会再次加载父类加载器加载过的类。
2、Java中的类大致分为三种
(1)系统类:(jre/lib/rt.jar)
(2)扩展类:(jre/lib/ext.jar)
(3)自定义类:(classpath指定目录下的类)
3、类加载的两种方式
(1)隐式加载
通过new等方式生成类或者子类对象,隐式调用类加载器加载对应的的类到JVM中。
(2)显示加载
通过调用Class.forName()或者ClassLoader.loadClass(className)等方法,显式加载需要的类。
4、Java的三大加载器
BootStrap Loader(根加载器) ----- 负责加载系统类
ExtClassLoader(扩展类加载器) ----- 负责加载扩展类
AppClassLoade(应用类加载器) ----- 负责加载应用类
5、三大加载器的搜索路径
Bootstrap Loader:sun.boot.class.path
ExtClassLoader:java.ext.dirs
AppClassLoader:java.class.path
6、类加载的机制原理
遵循原则:程序启动,基础类一次性加载,其他类按需加载。
当有类需要被加载时,子类加载器会请求父类加载器完成这个载入操作,父类会根据其自己的搜索路径来搜索需要被加载的类,如果搜索不到,才会由子类按照其搜索路径来完成载入操作。
父子类关系:(子)应用类加载器 -->扩展类加载器–>根加载器(父)
7、总结类加载的步骤
(1)装载:根据查找路径找到向对应的class文件,然后导入
(2)链接:链接可以分为三个小步骤
检查:检查待加载的class文件的正确性
准备:给类中的静态变量分配存储空间
解析:将符号引用转换成直接引用
(3)初始化:对静态变量和静态代码块执行初始化操作

66. 垃圾回收器GC

1、垃圾回收器的任务
(1)分配内存
(2)确保被引用的对象的内存不被错误地回收
(3)回收不再被引用的对象的内存空间
2、GC如何知道对象是垃圾?
对于垃圾回收器来说,它使用有向图来记录和管理堆内存中的所有对象,铜鼓这个有向图就可以知道哪些对象是可达的,哪些对象是不可达的,所有不可达的对象都是垃圾。
比如下面这段程序
java public class Test { public static void main(String[] args) { Integer i1 = new Integer(1); Integer i2 = new Integer(2); i2 = i1; //other code } }
在这段程序中,i1的资源被i2引用,但显然i2这个指向的对象并没有被引用到,所以资源i2是不可达的,垃圾回收器就会认为这块内存不会再被使用了,就会回收这块内存。
3、常用的垃圾回收算法
(1)引用计数法
在堆中的每个对象都有一个引用计数器;当对象被引用时,引用计数器加1;当引用被置为空或离开作用域时,引用计数器减1。
(2)追踪回收算法
利用JVM维护的对象引用图,当启动程序的时候,从根节点开始遍历对象的引用图,同时标记遍历到的对象。当遍历结束后,未被标记的对象就是目前不被使用的对象,可以被回收了。
(3)压缩回收算法
把堆中活动的对象移动到堆中一端,这样就会在堆中另外一端留出很大的一块空闲区域,相当于对堆中的碎片进行了处理。虽然这种方法可以大大简化消除堆碎片的工作,但是每次处理都会带来性能的损失。
(4)复制回收算法
把堆分成两个大小相同的区域,在任何时刻,只有其中的一个区域被使用,直到这个区域被消耗为止,此时垃圾回收期会中断程序的执行,通过遍历的方式把所有活动的对象复制到另一个区域中,在复制的过程中它们是紧挨着布置的,从而可以消除内存碎片。在复制过程结束后程序会接着执行,直到这块区域被使用完,然后采用上面方法继续执行。
(5)按代回收算法-针对复制回收算法改良
复制回收算法的主要缺点是:每次算法执行时(一个区域使用被消耗完),所有处于活动状态的对象都要被复制,这样效率很低。由于程序中创建的大部分对象的生命周期都很短,只有一部分对象有较长的生命周期的特点
按代回收的主要思想如下:把堆分成两个或者多个子堆,每一个子堆被视为一代。算法在运行的过程中优先收集那些“年幼”的对象进行清除,把经过多次收集仍然存活的对象转移到高一级的堆里,减少对其的扫描次数。

67. Java的内存泄露问题

当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。那么问题来了,Java语言中不是引入了垃圾回收器吗?为什么还会出现内存泄露问题?
1、在Java语言中,判断一个内存空间是否符合垃圾回收的标准有两个
第一:给对象的引用变量赋予了控制null,不再有变量引用这快内存空间。
第二:给引用变量赋予了新值,重新分配了内存空间,旧内存空间没有引用。
2、内存泄露的主要情况
java Vector v = new Vector(10); for(int i = 1; i<10; i++) { Object o = new Object(); v.add(o); }
在上述例子中,不断创建新的对象加到Vector对象中,当退出循环后,o的作用域即将会结束,但是由于v在使用这些对象,因此垃圾回收期无法将其回收,此时就造成了内存泄露。只有将这些对象从Vector中删除才能释放创建的这些对象。
(1)静态集合类
如HashMap、LinkedList等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。简单而言,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。
(2)各种连接
如数据库连接、网络连接和IO连接等。在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement或ResultSet不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。
(3)监听器
在Java语言中,往往会使用监听器。通常一个应用中会用到多个监听器,但在释放对象的同时往往没有相应删除监听器,这也可能造成内存泄露
(4)变量不合理的作用域
一般而言,一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为null,很有可能导致内存泄漏的发生。

```java 
public class UsingRandom {
		private String msg;
		public void receiveMsg(){
		readFromNet();// 从网络中接受数据保存到msg中
		saveDB();// 把msg保存到数据库中
		}
}
```
如上面这个伪代码,通过readFromNet方法把接受的消息保存在变量msg中,然后调用saveDB方法把msg的内容保存到数据库中,此时msg已经就没用了,由于msg的生命周期与对象的生命周期相同,此时msg还不能回收,因此造成了内存泄漏。
实际上这个msg变量可以放在receiveMsg方法内部,当方法使用完,那么msg的生命周期也就结束,此时就可以回收了。还有一种方法,在使用完msg后,把msg设置为null,这样垃圾回收器也会回收msg的内存空间。

(5)改变哈希值
当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。

---------------------------------------------------容器------------------------------------------------------

68. Java Collections框架是什么?

Java Collections框架中包含了大量集合接口以及这些接口的实现类和操作它们的方法(例如:排序,查找、、反转、替换、复制、取最小元素等)。
public static void sort(List list) : 排序,默认是自然排序(排序算法是修改的归并排序算法)
public static int binarySearch(List<?> list,T key): 二分查找(前提元素有序通过sort(List)方法)
public static T max(Collection<?> coll) :获取最大值
public static T min(Collection<?> coll) :获取最小值
public static void reverse(List<?> list) :反转(此方法以线性时间运行)
public static void shuffle(List<?> list) : 随机置换(使用默认源对指定列表进行置换)

69. 什么是迭代器?

迭代器(Iterator)是一个对象,它的工作是遍历并选择序列中的对象,它提供了一种访问一个容器对象中的各个元素,而又不必暴露该对象的内部细节的方法。通过迭代器,开发人员不需要了解容器底层的结构,就可以实现对容器的遍历。
1、迭代器的主要使用方法
(1)使用容器的Iterator()方法返回一个Iterator,然后通过Iterator的next()方法返回第一个元素。
(2)使用Iterator的hasNext()方法判断容器中是否还有元素,如果有,可以使用next()方法获取下一个元素。
(3)可以通过remove()方法删除迭代器返回的元素。
2、迭代器的ConcurrentModifiedException异常
原因:使用Iterator遍历容器的同时又对容器做增加或者删除所导致的,或者由于多线程操作导致的,当一个线程使用迭代器遍历容器的同时,另外一个线程对这个容器进行了增加或删除操作。
当调用容器的Iterator()方法返回Iterator对象时,把容器中包含的对象的个数赋值给一个变量expectedModCount,在调用next()方法时,会比较变量expectedModCount与容器中实际对象的个数modCount的值是否相等,若不相等则抛出ConcurrentModifiedException异常
3、单线程如何解决并发修改异常?
在遍历的过程中把需要删除的对象保存到一个集合中,等遍历结束后调用removeAll()方法来删除,或者iter.remove()方法
4、多线程如何解决并发修改异常?
(1)JDK1.5引入线程安全的容器,比如ConcurrentHashMap和CopyOnWriteArrayList。可以使用这这些线程安全的容器来代替非线程安全的容器。
(2)在使用迭代器遍历容器时对容器的操作放到synchronized代码块中,但是当引用程序并发程序比较高时,这会严重影响程序的性能。

70. ArrayList、Vector和LinkedList的区别

ArrayList、Vector和LinkedList类均在java.utl包中,均为可伸缩数组,即可动态改变长度的数组。
1、ArrayList和Vector的共同点
ArrayList和Vector都是基础存储元素的Object[ ] array来实现的,都支持下标访问元素因此索引快。
2、ArrayList和Vector的区别
(1)动态扩容的时候ArrayList默认扩充1.5倍,Vector默认扩充2倍
(2)它们的最大区别是synchronized(同步)的使用。没有一个ArrayList的方法是同步,而Vector的绝大多数方法(例如:add、insert、remove、set、equals、hashcode等)都是直接或间接同步的。所以Vector是线程安全的,ArrayList不是线程安全的。
3、LinkedList的特点
它是采用双向列表来实现的,对数据的索引需要从列表头部开始遍历,因此随机访问效率低,但是插入元素不需要对数据进行移动,因此插入效率高
4、如何选择使用ArrayList、Vector和LinkedList
对数据的主要操作为索引或者集合末端添加元素:建议使用ArrayList
对数据的操作主要为指定位置上插入或者删除元素:建议使用LinkedList
多线程使用容器时:建议使用Vector

71. HashMap、HashTable、TreeMap和WeakHashMap有哪些区别?

1、HashMap与HashTable的区别
(1)HashMap是HashTable的轻量级实现(非线程安全的实现),它们都完成了Map接口,主要区别在于HashTable允许空(null)键值(key)为null,不允许多条记录的值为null,而HashTale不允许为null。
(2)HashTable的方法是线程安全的,而HashMap不支持线程的同步,所以它不是线程安全的。在多个线程访问HashTable时,开发人员不需要对它进行同步,而对于HashMap,开发人员必须提供额外的同步机制。
(3)HashTable使用Enumeration遍历,HashMap使用Iterator遍历
(4)hash值的使用不同,HashMap使用键的hash值,,HashTable使用对象的hash值。
2、TreeMap的介绍
HashMap里面存入的键值对在取出时没有固定的顺序,是随机的,由于TreeMap实现了SortMaori接口,能够把它保存的记录根据键排序,因此取出来的顺序是排序的键值对。
如果需要按自然顺序或者自定义顺序遍历键,那么TreeMap更好
3、LinkedHashMap的介绍
LinkedHashMap是HashMap的一个子类,如果需要输出的顺序和输入的顺序相同,那么可以用它
4、WeakHashMap的介绍
WeakHashMap与HashMap类似,不同之处在于WeakHashMap中key采用的是“弱引用“的方式,只要WeakHashMap中的key不再被外部引用,它就可以被垃圾回收器回收。
而HashMap中key采用的是“强引用”的方式,当HashMap中的key没有被外部引用时,只有在这个key从HashMap中删除后才可以被垃圾回收器回收。

72. 用自定义类型作为HashMap或者HashTable的key需要注意什么问题?

我们知道HashMap与HashTable都是存储键值对的容器,并且不能存储重复的键。所以当我们的自定义类型的键需要重写equals和hashcode方法,如果不重写equals方法,那么会默认用Object的equals方法:比较是不是同一个对象,显然new出来的对象都是不同的,所以程序认为不是重复的,就分别创建不同的映射关系。所以必须重写hashcode和equals方法。

-------------------------------------------------------多线程------------------------------------------------------------

73. 进程、线程的概念以及多线程的好处

进程:一段正在执行的程序
线程:程序执行过程中,能够执行程序代码的一个执行单元,是程序执行的最小单元。
1、多线程的好处
(1)使用多线程可以减少程序的响应时间。比如将耗时的操作分配到一个独立的线程执行。
(2)与进程相比,线程的创建和切换开销更小。
(3)充分发挥多核CPU的多线程能力
(4)多线程使得程序便于理解和维护,模块更加清晰。

74. 同步和异步有什么区别?

同步:
可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。
异步:
执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。
比如拿叫人吃饭的例子:

我叫你去吃饭,你不出来吃饭我就一直叫,一直叫到你出来吃饭我才去吃饭,这就是同步
我叫你去吃饭,我叫了你几声,不管你出不出吃饭,我都不等你,自己先去吃饭了,这就是异步

去银行取款,ATM排队取款,你需要一直排队等待,直到轮到你才能取款。这就是阻塞
去银行取款,先去取号,你可以先去坐着或者干其他事,等到叫你再去取款。这就是非阻塞

你在家做饭,用普通的汤锅,米放进去,就站在锅边,傻等饭熟。——这叫同步阻塞
单任务按顺序执行。

用普通的汤锅,米放进去,然后继续回去打游戏,过一会就来看一次。——这叫同步非阻塞
多任务,定时查看任务执行状态。

淘了个电饭锅,饭熟了会自动跳闸。米放进去,然后傻傻的看它怎么做饭的。——这叫异步阻塞
单任务,自动提交任务执行状态。

米放进去,按下按钮,然后就去打游戏了,等到跳闸的时候就去吃饭 。——这叫异步非阻塞
多任务,自动提交任务执行状态,合理分配,最大化利用资源。

同步阻塞,顺序执行,只能傻等,效率低下 。
同步非阻塞,稍微高明点,但是麻烦了很多,多做很多无用功。
异步阻塞,这个等于自断一臂,没啥大意义。
异步非阻塞,这才是异步的最佳用法。

75. 如何实现Java多线程

1、实现多线程的三种方式
(1)继承Thread类,重写run()方法
(2)实现Runnable接口,实现run()方法,该实现类作为Thread的参数
(3)实现Callable接口,实现call()方法
2、Runnable与Callable的区别
Callable接口实际是属于Executor框架的功能类,与Runnable接口功能类似,但功能更强大
(1)Callable可以在任务结束后提供一个返回值,Runnable接口不能
(2)Callable中的call()方法可以抛出异常,Runnable接口的run()方法不能
注意:调用start()方法后,不是立即执行代码,而是使得该线程变为可运行态,什么时候运行线程代码是操作系统决定的。

76. 一个类是否同时继承Thread与实现Runnable接口?

答案:可以。示例如下:

public class Hello extends Thread implements Runnable{
    public static void main(String[] args) {
        Hello hello = new Hello();
        hello.start();
    }
}

在这里插入图片描述
结果发现,确实是可以编辑运行成功的。
你是不是有这样的疑问:Hello继承了Runnable接口,但却没有实现run()方法,为什么不会导致编译错误呢?
其实Hello类继承Thread类的时候继承了run()方法,这个继承的如()方法可以被当做Runnable接口的实现,所以是能够够编译通过的,但我们也可以重写run()方法。

77. run()方法与start()方法有什么区别?

通常,系统通过调用线程类的start()方法来启动一个线程,此时该线程处于就绪状态,而非运行状态,也就意味着这个线程可以被JVM调度来执行。在调度过程中,JVM通过调用线程类的run()方法来完成实际的操作,当run()方法结束后,该线程就会终止。
如果直接调用线程类的run方法,这会被当做一个函数来调用,并没有开启一个新线程,程序中仍然只有主线程这个线程。
也就是说:start()方法才是真正开启一个新的线程,并能够异步调用run()方法;仅仅调用run()方法仅仅是执行这个函数,而无法开启一个新的线程。

78. 多线程同步的实现方法有哪些?

1、synchronized关键字
当一个线程调用对象的一段synchronized代码时,需要先获取这个锁,然后去执行相应的代码,执行结束后,释放锁。
(1)synchronized方法
(2)synchronized代码快
2、Object对象的wait()和notify()方法
3、Lock锁
JDK5新增了Lock接口以及它的实现类ReentrantLock(重入锁),Lock也可以用来实现多线程的同步,具体而言,它提供了如下一些方法来实现多线程的同步。
(1)lock()。以阻塞(一直等待获取)的方式获得锁,也就是说,如果获得了锁,立即返回;如果别的线程持有锁,当前线程等待,知道获取锁后返回。
(2)tryLock()。以非阻塞的方式获取锁。只是尝试地去获取一下锁,如果获取到锁,立即返回true,否则立即返回false。
(3)tryLock(Long timeout,TimeUnit unit)。如果获取了锁,立即返回true,否则会等待参数给定的时间单元,在等待的过程中,如果获取了锁,就立即返回true,如果等待超时返回发了。
(4)lockInterruptibly()。如果获取了锁,立即返回;如果没有获取锁,当前线程处于休眠状态,直到获取锁。或者当前线程被别的线程中断(会收到InterruptedException异常)。它与lock()方法最大的区别在于如果lock()方法获取不到锁,会处于阻塞状态,且忽略interrupt()方法。

79. sleep()方法和wait()方法的区别

1)原理不同
sleep() 方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会(CPU) 给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。
wait() 方法是object类的方法,用于线程间的通信,这个方法会使当前持有该对象锁(可以定定义一个成员变量或者使用当前对象)的线程等待,直到其他线程调用notify或者notifyAll()方法唤醒。
2)对锁的处理机制不同
当调用了sleep() 方法,线程没有释放对象的锁,其他线程依然无法访问这个对象。
当wait()方法被调用后,线程会释放掉它所占的锁,从而其他线程可以访问这个这个对象。
3)使用的区域不同
wait()方法必须放在同步方法合作同步代码块中,sleep()方法可以在任何地方使用
4)异常抛出
sleep()方法必须捕获异常,wait()、notify()、notifyAll()不需要捕获异常

80. sleep()与yeild()方法的区别

1)sleep()方法给其他线程运行机会不考虑线程的优先级,因此会给优先级低的线程以运行的机会,而yeild()方法只会给相同优先级或者更高优先级的线程以运行的机会。
2)线程执行sleep()方法会进入阻塞状态,所以,执行sleep()方法的线程在指定的时间内肯定不会被执行,而yeild()方法只是使当前线程重新回到可执行状态,所以执行yeild()方法的线程有可能在进入到可执行状态后又马上被执行。
3)sleep()方法声明抛出InterruptException,而yeild()放阿飞没有声明任何异常
4)sleep()方法比yeild()方法具有更好的可移植性(更操作系统有关)。

81. 终止线程的方法有哪些?

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
    当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务,所以我们需要设置一个标志位来控制run方法的执行。
package chapter2;
public class ThreadFlag extends Thread
{
	//volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值,
    public volatile boolean exit = false;
    public void run()
    {
        while (!exit);
    }
    public static void main(String[] args) throws Exception
    {
        ThreadFlag thread = new ThreadFlag();
        thread.start();
        sleep(5000); // 主线程延迟5秒
        thread.exit = true;  // 终止线程thread
        thread.join();
        System.out.println("线程退出!");
    }
}
  1. 使用stop方法强行终止线程
thread.stop();

这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果
3. 使用interrupt方法中断线程。
使用interrupt方法来终端线程可分为两种情况:
(1)线程处于阻塞状态,如使用了sleep方法.
这种情况下使用interrupt方法,sleep方法将抛出一个InterruptedException
(2)使用while(!isInterrupted()){……}来判断线程是否被中断。
这种情况下使用interrupt方法,sleep方法将抛出一个InterruptedException例外

82. synchronized与Lock有什么异同

synchronized与Lock都是Java语言提供的两种锁机制来实现对某个共享资源的同步。
其中synchronized使用Object对象本身的notify、wait、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度。
1)用法不一样
synchronized关键字既可以加在方法上,锁的就是当前对象;也可以加载代码块上,括号中表示锁的对象,其是通过调用Object对象的wait、notify、notifyAll线程之间的调度。
Lock锁是通过lock.lock()和lock.unLock()进行锁定的,其线程之间的通信是通过Condition完成调度
2)性能不一样
在JDK5增加的Lock锁接口的实现类ReetantLock,它不仅拥有和synchronized相同的并发性和内存定义,还有锁投票、定时锁、等候和中断锁。
竞争不是很激烈的情况下,synchronized的性能由于ReentrantLock,
竞争很激烈的情况下,synchronized的性能下降很快,而ReentrantLock的性能几乎不变。
3)锁机制不一样
synchronized的锁是自动释放的,不会因为出现了异常导致锁没有被释放而引发死锁
lock锁的释放需要开发人员在finally语句块中释放,否则会引发死锁。它的tryLock()可以采用非阻塞的方式去获取锁。
最好不要同时使用这两个锁,因为它们的机制冉,运行都是独立的,互不影响。

83. 什么是守护线程

Java提供了两种线程:守护线程与用户线程。守护线程有被称为“服务线程” ”精灵线程“ 或 ”后台线程“,是指在程序运行时在后台提供通用服务的线程。通俗一点来说:任何一个守护线程都是整个JVM中所有非守护线程的保姆。
当用户线程全部退出时,程序退出,同时杀死所有的守护线程。
没有用户线程等于没有了程序和守护线程。
1、如何设置守护线程
将用户线程设置为守护线程的方法就是在调用start()方法启动线程之前调用对象的setDaemon(true)方法,参数为false则为用户线程。
2、守护线程的例子
守护线程的一个典型例子就是垃圾回收器。只要JVM启动,它就始终在运行,实时监控和管理系统中可以被回收的资源。

84. join()方法的作用是什么?

书中的解释是:join()方法就是指调用该方法的线程在执行完run()方法后,再执行join方法后面的代码,即将两个线程合并,用于实现同步控制。
具体作用:等待该线程终止,例如,在子线程调用了join(time)方法后,主线程只有等待子线程time时间后才能执行子线程后面的代码。

public class joinTest {
	class ThreadImp implements Runnable {
		public void run(){
			try{
				System.out.println("Begin");
				Thread.sleep(1000);
				System.out.println("end");
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	//主线程
	public static void main(String[] args) {
		joinTest ts = new joinTest();
		//子线程
		Thread t = new Thread(ts.new ThreadImp());
		t.start();
		try{
			//父线程main等待子线程t2秒后才能执行后面的代码
			t.join(2000);
			if (t.isAlive()) {
				System.out.println("alive");
			} else {
				System.out.println("dead");
			}
			System.out.println("finished");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

-----------------------------------------------------------------Java数据库操作------------------------------------------------------------------

85. 如何通过JDBC访问数据库

1)加载JDBC驱动器到classpath下
2)加载JDBC驱动并将其注册到DriverManager中。一般使用Class.forName(StringdriverName)

Class.forName("com.mysql.jdbc.Driver")

3)建立数据库连接

Connection con = DriverManager.getConnecttion(url,username,password)

4)获取statement对象

Statement stmt = con.createStatement()

5)执行SQL语句

stmt.execute("insert into student value (1,'wwj',25)")

6)获取并访问结果集ResultSet

ResultSet sr = stmt.executeQuery("select * from student");
while(rs.next()){}

86. JDBC 处理事务采用什么方法

事务是由多条SQL语句组成不可分割的工作单元,一般而言,JDBC是通过commit(提交事务)和rollback(异常回滚事务)方法来完成事务的操作。一般而言,JDBC的事务默认是自动提交的
1、设置自动提交为手动提交
通过setAutoCommit(false)方法来禁止自动提交,异常捕获中调用rollback回滚事务。
2、JDBC的事务隔离级别
1)TRANSACTION_NONE_JDB 不支持事务。
2)TRANSACTION_READ_UNCOMMITED 未提交读, 前一个事务可以看到另一个事务变化。此级别允许脏读、不可重复读、虚读。
3)TRANSACTION_READ_COMMITED 已提交读,说明读取未提交的数据是不允许的。此级别允许不可重复读、虚读。
4)TRANSACTION_REPEATABLE_READ 可重复读,说明事务保证能够再次读取相同的数据而不会失败。解决了不可重复读,但虚读仍会出现。
5)TRANSACTION_SERIALIZABLE 可序列化。事务最高隔离级别,可以防止脏读、不可重复读、虚读。
事务隔离级别越高,则为避免冲突所花费的资源消耗越高。可以通过conn.setTransactionLevel()方法来设置隔离级别,通过conn.getTransactionIsolation()方法确定当前事务的级别,。
备注:
①脏读:一个事务读到了另一事务的尚未提交的数据。
②不可重复读:一个事务的操作导致另外一个事务前后两次读到不同数据。
③虚读:一个事务的操作导致另外一个事务前后两次查询结果的数据量不同。

87. Statement、PrepareStatement和CallableStatement有什么区别

Statement用于执行不带参数的简单SQL语句,并返回它所生成结果的对象
每次执行SQL时,数据库都要编译该SQL语句。

Statemet stmt = conn.getConnection();
stmt.executeUpdate("insert into client values ('a','aaaa'));

PrepareStatement表示预编译的SQL语句的对象,用于执行参数的预编译SQL。

PrepareStatement stmt = con.PrepareStatement("select * from sutdent where id = ?")
stmt.setInt(1,1);
ResultSet = rs = stmt.executeQuery();

CallableStatement则提供了用来调用数据库中存储过程的接口,如果有输出参数要注册,说明是输出参数。
1、PrepareStatement比Statement的优势
除了能够完成相同的功能,还具备一下优点
1)效率更高
使用PrepareStatement执行SQL时,命令会被数据库进行编译和解析,并放到缓存区中。同一个SQL命令只需再次解析无需编译,可重复使用,提高执行效率
2)代码可读性和可维护性更好
PrepareStatement通过set方法让SQL语句携带参数,可读性和可维护性更好。

prestmt = con.preprareStatement("insert into tb_name (col1,col2) values (?,?)");
prestmt.setString(1,var1);
prestmt.setString(2,var2);

Statement的SQL语句携带参数的话仅一条语句,可读性差

stmt.executeUpdate("insert into t (col1,col2) values ('"+var1+"','"+var1+"')")

3)安全性更好
使用prepareStatement能够预防SQL注入。

能够避免这种情况:select * from user where name='aa' and password='bb' or 1=1

88. getString()方法和getObject()方法有什么区别

getString()或getInt()等方法在被调用时,程序会一次性把所有数据都放到内存中,然后通过调用ResultSet的next()和getString()等方法来获取数据。当数据量大到内存中放不下时就会抛出异常,而使用getObject()方法就不会有这种问题,因为数据不会一次性被读到内存中。每次调用时会直接从数据库中去获取数据,因此使用这种方法不会因为数据量过大而出错。

------------------------------------------------JavaWeb---------------------------------------------------
若对概念不理解,可细读:理解Servlet和Servlet容器、Tomcat容器、Web服务器等概念

88. Tomcat服务器接受客户请求并做出响应的过程

1)客户端(通常都是浏览器)访问Web服务器,发送HTTP请求。
2)Web服务器接收到请求后,传递给Servlet容器。
3)Servlet容器加载Servlet,产生Servlet实例后,向其传递表示请求和响应的对象。
4)Servlet实例使用请求对象得到客户端的请求信息,然后进行相应的处理。
5)Servlet实例将处理结果通过响应对象发送回客户端,容器负责确保响应正确送出,同时将控制返回给Web服务器。

88. Tomcat是什么

Tomcat是Java Servlet,JavaServer Pages,Java Expression Language和Java WebSocket(Java EE)技术的开源实现。
因为可以通过HTTP提供HTML页面等静态内容的请求访问,所以是一个WEB服务器;
因为实现了Servlet规范,所以也是一个Servlet容器,可以运行Servlet程序;
因为可以通过Servlet容器,调用Servlet处理动态请求,所以也是一个应用服务器;
所以,可以说Tomcat是Java(EE) WEB应用服务器。

89. JSP的内置对象有哪些

在JSP中,内置对象又称为隐含对象,是指在不声明和不创建的情况下就可以被使用的一些成员变量。JSP一共提供了9个内置对象。

  • request:请求对象:获取请求参数,连接请求的内容
  • response:响应对象:将Web服务器处理后的结果返回给客户端
  • pageContext:JSP的上下文对象:提供了对JSP页面的所有对象以及命名空间的访问
  • session:会话对象:表示客户端与服务去的一次会话,关闭浏览器结束会话
  • application:ServletContext对象:代表着JSP所属的Web应用本身。application对象可存放全局变量,因此可以实现用户间的数据共享。
  • config:ServletConfig对象:主要作用是取得服务器的配置信息。当一个Servlet初始化时,容器把某些信息通过config对象传递给这个Servlet,Servlet可以使用这个对象获取所需的配置信息
  • out:JSP输出流对象:用于在客户端输出信息。它是JSPWriter类的实例
  • page:指向当前JSP的对:表示当前JSP页面,类似于Java的this指针。它是Java.lang.Object类的实例
  • exception:异常对象:用来表示异常。当一个页面在运行过程中发生的例外,就会产生这个对象。如果JSP需要使用这个对象,就必须把isErrorPage设为true,否则编译无法通过。

根据分类,可以分为四大类

  • 与Servlet有关的:page、config
  • 与Input、Output有关的:out、request、response
  • 与Context有关的:application、session、pageContext
  • 与Error有关的:exception

request对象的主要方法有哪些?

setAttribute(String name,Object):设置名字为name的request 的参数值

getAttribute(String name):返回由name指定的属性值

getAttributeNames():返回request 对象所有属性的名字集合,结果是一个枚举的实例

getCookies():返回客户端的所有 Cookie 对象,结果是一个Cookie 数组

getCharacterEncoding() :返回请求中的字符编码方式

getContentLength() :返回请求的 Body的长度

getHeader(String name) :获得HTTP协议定义的文件头信息

getHeaders(String name) :返回指定名字的request Header 的所有值,结果是一个枚举的实例

getHeaderNames() :返回所以request Header 的名字,结果是一个枚举的实例

getInputStream() :返回请求的输入流,用于获得请求中的数据

getMethod() :获得客户端向服务器端传送数据的方法

getParameter(String name) :获得客户端传送给服务器端的有 name指定的参数值

getParameterNames() :获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实

getParameterValues(String name):获得有name指定的参数的所有值

getProtocol():获取客户端向服务器端传送数据所依据的协议名称

getQueryString() :获得查询字符串

getRequestURI() :获取发出请求字符串的客户端地址

getRemoteAddr():获取客户端的 IP 地址

getRemoteHost() :获取客户端的名字

getSession([Boolean create]) :返回和请求相关 Session

getServerName() :获取服务器的名字

getServletPath():获取客户端所请求的脚本文件的路径

getServerPort():获取服务器的端口号

removeAttribute(String name):删除请求中的一个属性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值