java面试题(java重要知识点)整理

java基础

  1. java创建对象的方式有几种?

4种

①通过new语句实例化一个对象

②通过反射机制创建对象

③通过clone()方法创建一个对象

④通过反序列化的方式创建对象

  1. java异常

java异常Error(错误)和Exception(异常)两大类,他们拥有一个共同的父类Throwable

error表示程序在运行期间出现了非常严重的错误,这种错误会导致程序终止执行。需要人为解决,编译器不会去检查error是否被处理。人话:一个正确的程序中不应该存在error,否则JVM一般会选择将线程终止,然后需要程序员去解决这个错误。OutOfMemoryError、ThreadDeath等都属于Error。

Exception表示可恢复的异常,是编译器可以捕捉到的,包括检查异常(checked exception)和运行时异常(runtime exception)。

(1)检查异常

检查异常是经常遇到的异常,所有继承自Exception并且不是运行时异常的异常都是检查时异常。这种异常都发生在编译阶段,java编译器强制程序去捕获此类异常,即把可能出险异常的代码放到try中,把对异常的处理放到catch中。比较常见的有IO异常和SQL异常。

(2)运行时异常

运行时异常和检查异常不同,编译器不会对此类异常强制捕获并处理。如果不对这种异常进行处理,当出现这种异常时一般或交给JVM来处理,表现为在程序运行时报错,线程可能会终止。常见的有NullpointerException(空指针异常),ClassCastExceprion(类型转换异常),ArrayIndexOutOfBoundsException(数组越界异常),ArrayStoreException(数组存储异常),BufferOveeflowException(缓冲区溢出异常),ArithmeticException(算术异常)。

注意:

异常处理用到了多态的概念,在进行异常捕获时,应该先捕获子类,再捕获基类的异常信息,否则捕获子类的代码块永远不会被执行。

  1. 可不可以在一个静态方法内调用一个非静态方法?

不可以。

类的静态成员(方法和属性)属于类本身,在类加载的时候就会分配内存,可以通过类名.属性名直接访问。

非静态成员(方法和属性)属余类对象,在类加载的时候不会分配内存,只有生成类的对象后才会分配内存,通过类的对象来访问。

所以类的静态方法存在时,类的非静态方法可能不存在,所以在静态方法中调用非静态方法时非法的。

  1. 重载和重写的区别

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,②比父类被重写方法更好访问(访问权限不能比父类中被重写的方法的访问权限更低),不能比父类被重写方法声明更多的异常(里氏代换原则)。 构造方法不能被重写,但能被重载,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是能够被再次声明。

重载重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载。

方法重载的规则:

①.方法名一致,参数列表中参数的顺序,类型,个数不同。

②重载与方法的返回值无关,存在于父类和子类,同类中。

③可以抛出不同的异常,可以有不同修饰符

  1. final、finally和finalize有什么区别

final用于声明属性、方法和类,分别表示属性不可变,方法不可覆盖和类不可被继承。被final修饰的变量不可变,但是变量有两种含义,一种是引用不可变,一种是对象不可变,final指的是引用的不可变性,即它只能指向初始时指向的那个对象,而不关心对象内容的变化。

一个类,不能既被声明为abstract,又被声明为final。

finally作为异常处理的一部分,只能用在try/catch语句中,不管程序是否出现一场,finally中的代码一定会被执行(有两种情况不会被执行,一是在进入try/catch代码前遇到异常,而是在try中调用systen.exit(0),程序强制退出)。

finalized是Object的一个方法,在垃圾回收器执行时会调用被回收对象的finalized()方法,可以覆盖此方法来实现对其他资源的回收,例如关闭文件等。一旦垃圾回收器准备好释放对象的内存空间,将首先调用其finalized()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。7

  1. 成员变量与局部变量的区别在哪里

1)修饰符方面:成员变量可以被public、private、static等修饰符修饰,而局部变量不能被访问控制修饰符以及static修饰;但是,成员变量个局部变量都能够被final修饰。

2)存储方式方面:成员变量:如果使用static修饰,那么这个成员变量是属于类的,如果不是,这个成员变量是属于类实例的。如果局部变量的类型为基本类型,那么是存储在栈中的,如果为引用数据类型,则是指向堆内存中对象的引用或者是指向常量池中的地址。

3)内存中生存时间方面:成员变量是类(使用static修饰)或者对象(没有使用static)的一部分,随着类的加载或者对象的创建而产生,局部变量是随着方法的调用而产生,调用后销毁。

4)初始值方面:成员变量如果没有初始值,则会自动一类型的默认值赋值(例外:被final修饰的成员变量必须赋值),而局部变量不会。

5)线程安全:局部变量线程安全,成员变量如果所在对象是单例,则想成不安全,如果是多例,线程安全。

  1. 序列化与反序列化

对象的生命周期随着JVM的停止而结束,但在实际应用中,就可能需要在JVM停止后能够保存(持久化)某个对象,并在以后重新读取被持久化的对象,这个时候就会用到序列化。另外序列化也被用于网络传递和远程方法调用(RMI)。

java序列化是指把java对象转换为字节序列(字节数组)的过程。
好处:①把对象转化为字节流以便在应用间传输或者在本地保存或者保存到数据库,保证对象的完整性和可传递性。②对象、文件等不同的数据格式都可以使用序列化转化成字节流,统一传输或保存。
反序列化是指把字节序列恢复为java对象的过程。序列化前是什么类型的,反序列化后就是什么类型的。

如何实现序列化
在java中,一个类只要实现了java.io.Serializable接口,那么他就可以被序列化,使用ObjectoutputStream和ObjectInputStream可以对该类进行序列化和反序列化。另,使用writeObject和readObject方法可以自定义序列化策略。

  1. java对象中什么样的变量不能够被序列化。

① static修饰的静态变量。因为序列化是序列化对象的状态及信息,静态变量属于类而不属于对象。
② 使用transinet关键字修饰的变量。需要注意的是:反序列化后,被transinet修饰的变量会默认为对应类型的初始值,如int类型反序列化后就是0,如果对应的类型是对象,则设为null。

  1. 内存泄漏与内存溢出

内存泄漏:指一个不再被使用的程序使用的对象或变量还在内存中占有存储空间。内存泄漏有两种情况,一是在堆中申请的内存没有被释放,二是对象已不再被使用,但还是在内存中保留着。
造成内存泄漏的情况举例:
①静态集合类,例如HashMap和Vector,由于是静态的,所以他们的生命周期和JVM是同样的,那么集合中所有的对象都不会被释放,从而造成内存泄漏。
②各种连接,如数据库连接,网络连接,IO连接等没有及时close。
③变量不合理的作用域。如果一个对象定义的作用范围大于其使用范围,没有及时把对象设置为null,很有可能会造成内存泄漏。
④Threadlocal 变量没有及时remove。

内存泄漏:
内存泄漏不及时处理可能会造成内存溢出。
1.堆内存溢出
设置jvm值的方法是通过-Xms(堆的最小值),-Xmx(堆的最大值)
2.栈内存溢出
设置栈大小的方法是设置-Xss参数
3.永久区内存溢出
第三个异常是关于perm的异常内容,我们需要的是设置方法区的大小,实现方式是通过设置-XX:PermSize和-XX:MaxPermSize参数
4.DirectMemory
直接内存溢出,直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。但是这部分内存也被频繁的使用,而且也可能导致OutOfMemoryError异常出现。

数据库

  1. drop、delete与truncate的区别和使用场景?

drop 表示删除表内容和结构,语句后面不带where,不可回滚,删除速度快。
truncate 只删除表内容,语句后面没有where,不可回滚,删除速度快。
delete 删除指定内容,主要看where的条件情况,这个操作会放到 rollback segement 中,事务提交之后才生效,所以可回滚。

删除速度:drop> truncate > delete
如果不想要某个表了,直接drop,如果想要这个表,只是不想要里面的数据了,可以用truncate或delete(事务相关,可回滚)。

  1. 数据库设计
  1. 数据库和表的字符集统一使用 UTF8
    兼容性更好,统一字符集可以避免由于字符集转换产生的乱码问题
  2. 禁止在数据库中存储图片,文件等大的二进制数据
    数据库读取时会进行大量的随机IO操作,文件或者图片越大,IO操作越耗时。
  3. 数据库列选择合适的类型,并且大小不宜过大
    列的字段越大,建立索引时所需要的空间也就越大,这样一页中所能存储的索引节点的数量就越少,在遍历时所需要的 IO 次数也就越多,索引的性能越差。
  4. 避免使用 ENUM 类型定义字段
    使用ORDER BY,ENUM 枚举类型效率很低,需要额外的操作。
  5. 金钱相关的用decimal 类型
    float,double是非精准浮点型,decimal是精准浮点型,计算时不会丢失精度。
  6. 所以不宜过多,选择常用于查询条件的字段做索引
    索引越多占用空间越多,且维护成本增大,如果很多歌索引都能用于查询,MySql的优化器在选择最优执行计划时所需要的时间也就越多,降低查询性能。
  1. MySql中SQL语句执行用到的组件
  1. 连接器:进行用户身份和权限验证
  2. 查询缓存:用来缓存SELECT语句及语句查询到的结果集,使用key-value的形式存储,key为sql语句,value为结果集。不过缓存在实际场景中使用的不多,因为表数据的增加、更新、删除都需要清理缓存,MySql8.0以后的版本就去掉了缓存功能。
  3. 分析器:进行词法分析提取关键字,表名,查询条件等,知道这个sql要干嘛,然后进行语句分析,分析该条sql语句是否正确。
  4. 优化器:选择优化器任务最优的方案去执行sql,比如如何选择索引最好,但这也只是执行器认为最优的方案,有可能并不是最优的。
  5. 执行器:优化器选择完方案后,执行器会先进行权限验证,如果没有权限就返回错误信息,如果有,则按照方案调用存储引擎的接口,并返回接口执行结果。
  1. SQL语句执行顺序
  1. 查询语句(select)
    权限验证->查询缓存->分析器分析语句->优化器选择最优执行方案->权限验证->执行器调用存储引擎接口查询结果并返回。
  2. 更新语句(insert、update、delete)
    分析器分析语句->权限验证->执行器调用存储引擎接口更改数据->预提交日志redo log->提交日志binlog->提交日志redo log。
    binlog:mySql自带的日志,所以存储引擎可以使用,用于归档数据,没有事务功能,所以mySql突然重启,不能恢复数据。
    redo log: InnoDB 引擎自带的一个日志模块,提供事务支持,数据库异常重启时,之前提交的记录不会丢失。
  1. SQL语句优化
  1. 在表中建立索引,优先考虑where.group by使用到的字段。
  2. 查询条件中,一定不要使用select*,因为会返回过多无用的字段会降低查询效率。应该使用具体的字段代替*,只返回使用到的字段。
  3. 不要在where条件中使用左右两边都是%的like模糊查询,如:
    SELECT * FROM t_order WHERE customer LIKE ‘%zhang%’
    这样会导致数据库引擎放弃索引进行全表扫描。
    优化:尽量在字段后面使用模糊查询。如下:
    SELECT * FROM t_order WHERE customer LIKE ‘zhang%’
  4. 尽量不要使用in 和not in,会造成全表扫描。如下:
    SELECT * FROM t_order WHERE id IN (2,3)
    SELECT * FROM t_order1 WHERE customer IN (SELECT customer FROM t_order2)
    优化:
    对于连续的数值,能用 between 就不要用 in ,如下:SELECT * FROM t_order WHERE id BETWEEN 2 AND 3
    对于子查询,可以用exists代替。如下:SELECT * FROM t_order1 WHERE EXISTS (SELECT * FROM t_order2 WHERE t1.customer = t2.customer)
  5. 尽量不要使用or,会造成全表扫描。如下:
    SELECT * FROM t_order WHERE id = 1 OR id = 3
    优化:可以用union代替or。如下:
    SELECT * FROM t_order WHERE id = 1
    UNION
    SELECT * FROM t_order WHERE id = 3
  6. 尽量不要在 where 子句中对字段进行表达式操作,这样也会造成全表扫描。如:
    select id FROM t_order where num/2=100
    应改为:
    select id FROM t_order where num=100*2
  7. where条件里尽量不要进行null值的判断,null的判断也会造成全表扫描。如下:
    SELECT * FROM t_order WHERE score IS NULL
    优化:
    给字段添加默认值,对默认值进行判断。如:
    SELECT * FROM t_order WHERE score = 0
  8. 尽量不要在where条件中等号的左侧进行表达式.函数操作,会导致全表扫描。如下:
    SELECT * FROM t_order2 WHERE score/10 = 10
    SELECT * FROM t_order2 WHERE SUBSTR(customer,1,5) = ‘zhang’
    优化:
    将表达式.函数操作移动到等号右侧。如下:
    SELECT * FROM t_order2 WHERE score = 10*10
    SELECT * FROM t_order2 WHERE customer LIKE ‘zhang%’
  9. 尽量不要使用where 1=1的条件
    有时候,在开发过程中,为了方便拼装查询条件,我们会加上该条件,这样,会造成进行全表扫描。如下:
    SELECT * FROM t_order WHERE 1=1
    优化:
    如果用代码拼装sql,则由代码进行判断,没where加where,有where加and
    如果用mybatis,请用mybatis的where语法。
  10. 程序要尽量避免大事务操作,提高系统并发能力。
  11. 一个表的索引数最好不要超过6个,如果索引太多的话,就需要考虑一下那些不常使用到的列上建的索引是否有必要。

框架

Spring

  1. Spring的Controller是单例还是多例?线程安全问题?

controller默认是单例的。单例是线程非安全的,所以要尽量避免在controller中使用成员变量,否则会发生数据错误的问题。
解决方案:
1)不要在controller中定义成员变量,如果必须的话,可以在controller上添加注解@Scope(“prototype”)使其变成多例(静态变量即使加了@Scope注解,也是非线程安全的)。
2)在controller中使用ThreadLocal变量。

  1. Spring特点

1)轻量级:从大小和开销方面来看,String都是轻量的,完整的Spring框架可以在大小只有1兆多的jar文件里发布。
2)控制反转:促进低耦合。使一个对象依赖的其他对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找要依赖的对象。
3)面向切面:面向切面编程,把应用业务逻辑和系统切面分开。应用:日志等。
4)容器:spring管理应用对象的配置和生命周期,起一个容器的作用。
5)与其他框架无缝整合:可以与spring mvc 、struts、mybatis等框架集成,将应用逻辑的开发留给程序员开发。

  1. Spring常用模块。

1.Spring Core: Spring的基础,Spring其他所有的功能都依赖于该类库,主要提供了IOC(依赖注入)功能。
2. Spring Aop: 提供面向切面编程的实现。
3. Spring JDBC: 数据库连接。
4. Spring ORM:提供了与Hibernate等ORM框架的支持。
5. Spring Web:为创建Web应用提供支持。
6. Spring JMS:java消息服务。
7. Spring Aspects:支持与Aspectj的集成。
8. Spring Test:提供对JUNIT等测试工具的支持。

  1. Spring常用注解

声明Bean使用:

@controller 使用它标记的类就是一个SpringMVC Controller 对象,即一个控制器类。该类可以分发用户的请求,接收请求数据,并选择恰当的视图以用于显示。
@ResponseBody 该注解将Controller的方法返回的对象转换为指定的数据格式返回,写入到Response对象的Body中,通常为json、xml等格式。
@RestController @controller与@ResponseBody的结合体,用于控制层。
@Service 用于注解业务层。
@Repository 用于注解数据库连接成(dao层)。
@Component 如上,当组件不好归类时,用@Compeonent将该主键标记成bean交给Spring管理。

注入Bean使用:

@Autowrite 注解在类成员变量,方法、构造函数上,完成自动装配工作,省去set get方法。
@Resource 作用和@Autowrite相同

➡️@Autowrite与@Resource的区别:

SpringMVC相关

@RequestMapping 用来处理请求地址映射的注解,用于方法上,也可用于类上,用于类上表示类中的方法都以改地址作为父路径。
@PathVariable 将接收请求url上的模板变量映射到请求方法的参数上。
@RequestBody 用于方法参数前面,用于接收request请求体中的参数。
@ModelAttribute

测试相关

@RunWith(SpringRunner.class)
@SpringBootTest

Aop相关

@Aspect 作用在类上,声明一个切面
使用@After、@Before、@Around定义建言(advice),三个都作用于方法上,可直接将拦截规则(切点)作为参数。
@After 在方法执行之后执行
@Before 在方法执行之前执行
@Around 在方法执行之前与之后执行
@PointCut 声明切点
在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持

其他:

@Value 为属性注入值 如
@Value(“kunkun”)
String name;

  1. Spring的四种依赖注入方式
    https://blog.csdn.net/zhaohong_bo/article/details/89916494
  2. Spring思想:控制反转与依赖注入

控制反转(IOC):
🔹谁控制谁:传统程序中我们在对象A中如果需要对象B就会使用new创建一个对象B,但是现在IOC专门有一个容器来创建对象,管理对象,即IOC容器控制了对象
🔹控制什么?控制了对象中(A)对外部资源(B)的获取。
🔹如何反转:先看正转,正转就是我需要什么我就去拿,A需要B,那就A主动去创建B,但现在,IOC容器帮我们创建、查找和注入我需要的对象,A变成被动接受依赖对象B。即由主动出击变成被动接接受。
👀所以控制反转就是:当一个对像中需要另一个对象时,它不再自己创建这个所需要的对象,而是把控制权交给IOC容器,由IOC容器来创建和注入我依赖的对象,我只是接收就行了。

依赖注入
🔹谁依赖于谁:应用程序依赖于IoC容器;
🔹为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
🔹谁注入谁:IoC容器注入程序某个对象;
🔹注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
👀所以依赖注入:某个对象需要某个外部资源,由IOC容器创建并注入这个被需要的外部资源。

依赖注入和控制反转是同一概念吗
  控制反转是从容器的角度在描述,容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。
  依赖注入是从应用程序的角度在描述:应用程序依赖容器创建并注入它所需要的外部资源
  从上面可以看出两者其实就是同一件事的不同描述,只不过角度不同

IoC/DI对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。这样就有效的分离了对象和它所需要的外部资源,使它们松耦合,有利于功能复用,使得程序的整体结构变得非常灵活。

  1. Spring事务传播机制有哪些?

1.PROPAGATION_REQUIRED:如果当前存在事务,就加入该事务,如果没有事务,就创建新的事务。通常是我们的默认选择。

2.PROPAGATION_REQUIRES_NEW:无论当前有没有事务,都新创建一个事务。

3.PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

4.ROPAGATION_NOT_SUPPORTED:以非事务的方式执行,如果当前存在事务,就把该事务挂起。

5.PROPAGATION_NEVER:以非事务的方式执行,如果当前存在事务,就抛出异常。

6.PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

7.PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

MyBatis

  1. #{}与${}的区别

#{} 是预编译的方式,相当于jdbc的占位符PrepareStatement,mybatis在设置值时,会加上单引号。
例如:select * from student where name = #{name},mybatis编译处理后就是:select * from student where name = ‘xxx’
${}不对数值做预编译,直接拼接,传过来是什么就是什么。
例如:select * from student where name = #{name},mybatis编译处理后就是:select * from student where name = xxx
这种方式是不安全的,存在sql注入的问题。所以一般不使用 $ {} 进行传值处理,除非遇到只能用${}的情况,如order by后面,或者是传表名,列明等:select * from ${tablename} where name = #{name} order by ${id}

预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值