1.数字签名和加密的区别是什么?
数字签名使用发送方的密钥对,发送方使用自己的私有密钥进行加密,而接收方只需要发送方的公开密钥就可以解密,是一种一对多的关系,只要持有发送方公开密钥的人都可以验证数字签名的正确性。
加密是一种以密码方式发送信息的方法,指的是如下这样一个过程:发送方利用接收方的公钥对要发送的明文进行加密,接收方利用自己的私钥进行解密,其中公钥和私钥是相对的,任何一个作为公钥,则另一个就为私钥。所以,加密使用的是接收方的密钥对,这是一种多对一的关系,任何知道接收方公开密钥的人都可以向接收方发送加密信息,只有唯一拥有接收方私有密钥的人才能对信息解密。
另外,数字签名采用的是非对称密钥加密算法,它能保证发送信息的完整性、身份认证和不可否认性,而数字加密采用的是对称密钥加密算法和非对称密钥加密算法相结合的方法。
2.在Java语言中,Socket的连接和建立的原理是什么?
在Java语言中,Socket可以分为两种类型:面向连接的Socket(Transmission Control Protocol,TCP,传输控制协议)通信协议和面向无连接的Socket(User Datagram Protocol,UDP,用户数据报协议)通信协议。任何一个Socket都是由IP地址和端口号唯一确定的
首先,Server端Listen(监听)指定的某个端口(建议使用大于1024的端口)是否有连接请求,然后,Client端向Server端发出Connect(连接)请求,紧接着,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了,会话随即产生。Server端和Client端都可以通过Send、Write等方法与对方通信。Socket的生命周期可以分为三个阶段:打开Socket、使用Socket收发数据以及关闭Socket。在Java语言中,可以使用ServerSocket作为服务端,Socket作为客户端来实现网络通信。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 龙御修
* @create 2022-04-11 19:38
*/
class Server {
public static void main(String[] args) {
BufferedReader br=null;
PrintWriter pw=null;
ServerSocket server=null;
try {
server=new ServerSocket(2000);
Socket socket=server.accept();
//获取输入流
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取输出流
pw=new PrintWriter(socket.getOutputStream(),true);
String s= br.readLine();//获取接收器的数据
pw.println(s);//发送相同的数据给客户端
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(br!=null)br.close();
if(pw!=null)pw.close();
if(server!=null)server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* @author 龙御修
* @create 2022-04-11 19:48
*/
public class Client {
public static void main(String[] args) {
BufferedReader br=null;
PrintWriter pw=null;
Socket socket=null;
try {
socket=new Socket("localhost",2000);
//获取输入流与输出流
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw=new PrintWriter(socket.getOutputStream(),true);
//向服务器发送数据
pw.println("Hello");
String s=null;
while (true){
s=br.readLine();
if(s!=null)
break;
}
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(br!=null)br.close();
if(pw!=null)pw.close();
if(socket!=null)socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.XML有哪些解析技术?它们有什么不同点?
方式有两种:DOM(Document Object Model,文档对象模型)和SAX(Simple API for XML,XML 简单 API)。其中,DOM方式会根据给定的XML文件在内存中创建一个树形结构,占用较多的内存,处理大文件效率低,解析文件之前就把文档装入内存,适用于对XML的随机访问与频繁地对内容进行修改。SAX是事件驱动型的XML解析方式,它不会在内存中存储XML文件的内容,只是把每次对数据的请求看成是一个事件,通过遍历文件来获取用户所需的数据。当遇到像文件开头、文档结束或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件。所以,它的使用场合一般为对XML的顺序访问、XML文件太大以至于在内存中放不下等情况。
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
/**
* @author 龙御修
* @create 2022-04-11 20:09
*/
public class DOMTest {
public static void main(String[] args) {
File f = new File("./test.fxml");
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(f);
NodeList nl=doc.getElementsByTagName("p");
for(int i=0;i<nl.getLength();i++){
System.out.print("姓名:"+doc.getElementsByTagName("name").item(i).getFirstChild().getNodeValue());
System.out.println("年龄:"+doc.getElementsByTagName("age").item(i).getFirstChild().getNodeValue());
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
/**
* @author 龙御修
* @create 2022-04-11 20:28
*/
public class SAXTest extends DefaultHandler {
public void characters(char[] ch,int start,int length)throws SAXException{
System.out.print(new String(ch,start,length));
super.characters(ch,start,length);
}
public void endDocument() throws SAXException{
super.endDocument();
}
public void endElement(String url,String localName,String qName)throws SAXException{
System.out.print("</"+qName+">");
super.endElement(url,localName,qName);
}
public void startDocument()throws SAXException{
System.out.println("<?xml version=\"1.0\"encoding=\"UTF-8\"?>");
super.startDocument();
}
public void startElement(String uri, String localName, String qName, Attributes attrs)throws SAXException{
System.out.print("<"+qName);
if(attrs!=null){
for(int i=0; i<attrs.getLength();i++){
System.out.print(" "+attrs.getQName(i)+"=\""+attrs.getValue(i)+"\"");
}
}
System.out.print(">");
super.startElement(uri,localName,qName,attrs);
}
public static void main(String[] args) throws Exception {
SAXParserFactory factory=SAXParserFactory.newInstance();
SAXParser parser=factory.newSAXParser();
File f=new File("test.fxml");
SAXTest dh=new SAXTest();
parser.parse(f,dh);
}
}
4.Java语言中有几种方法可以终止线程运行?stop()和suspend()方法为什么不推荐使用?
采用的方法是让线程自行结束,进入Dead、stop和suspend
在Java语言中,可以使用stop方法与suspend方法来终止线程的执行。当使用Thread.stop()方法来终止线程时,它会释放已经锁定的所有的监视资源。如果当前任何一个受这些监视资源保护的对象处于一个不一致的状态,其他的线程将会看到这个不一致的状态,这可能会导致程序执行的不确定性,并且这种问题很难被定位。suspend方法的使用容易引起死锁。由于调用suspend方法不会释放锁,这就会导致一个问题:如果使用一个suspend挂起一个有锁的线程,那么在锁恢复之前将不会被释放。如果调用suspend方法的线程试图取得相同的锁,程序就会发生死锁。
5.寻找一条从左上角(arr[0][0])到右下角(arr[m−1][n−1])的路线,使得沿途经过的数组中的整数之和最小。
/**
* @author 龙御修
* @create 2022-04-11 22:03
*/
//递归法
public class Demo02 {
public static int getMinPath(int[][] arr,int i,int j){
//倒着走到第一个节点,递归结束
if(i==0&&j==0)
return arr[i][j];
//选取两条可能路径上的最小值
else if(i>0&&j>0)
return arr[i][j]+Math.min(getMinPath(arr,i-1,j),getMinPath(arr,i,j-1));
//下面两个条件只有一条路可选
else if(i>0&&j==0)
return arr[i][j]+getMinPath(arr,i-1,j);
//j>0&&i==0
else
return arr[i][j]+getMinPath(arr,i,j-1);
}
public static int getMinPath(int[][] arr){
if(arr==null||arr.length==0)
return 0;
return getMinPath(arr,arr.length-1,arr[0].length-1);
}
public static void main(String[] args) {
int[][]arr={{1,4,3},{8,7,5},{2,1,5}};
System.out.println(getMinPath(arr));
}
}
/**
* @author 龙御修
* @create 2022-04-11 22:15
*/
//动态规划法
public class Demo04 {
public static int getMinPath(int[][]arr ){
if(arr==null||arr.length==0)
return 0;
int row=arr.length;
int col=arr[0].length;
//用来保存计算的中间值
int[][] cache=new int[row][col];
cache[0][0]=arr[0][0];
for(int i=1;i<col;i++){
cache[0][i]=cache[0][i-1]+arr[0][i];
}
for(int j=1;j<row;j++){
cache[j][0]=cache[j-1][0]+arr[j][0];
}
//在遍历二维素组的过程中不断把计算结果保存到cache中
for(int i=1;i<row;i++){
for(int j=1; j<col;j++){
//可以确定选择的路线为arr[i][j-1]
if(cache[i-1][j]>cache[i][j-1]){
cache[i][j]=cache[i][j-1]+arr[i][j];
System.out.print("["+i+","+(j-1)+"]");
}
//可以吧确定选择的路线为arr[i-1][j]
else {
cache[i][j]= cache[i-1][j]+arr[i][j];
System.out.print("["+(i-1)+","+j+"]");
}
}
}
System.out.println("["+(row-1)+","+(col-1)+"]");
return cache[row-1][col-1];
}
public static void main(String[] args) {
int[][] arr={{1,4,3},{8,7,5},{2,1,5}};
System.out.print("路径:");
System.out.println("最小值:"+getMinPath(arr));
}
}
2.使用两种方法编写多线程环境下的Singleton模式,并比较这两种方法的特性。
public class Singleton {
private static Singleton insstance;
private Singleton(){}
public static synchronized Singleton fetImstance(){
if(insstance==null){
insstance=new Singleton();
}
return insstance;
}
}
这种写法虽然是多线程安全的,但是每次使用getInstance方法都需要进行同步,因此效率比较低。
public class Singleton {
private volatile static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton==null){
synchronized (Singleton.class) {
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
这种方法会首先判断singleton是否为空,如果这个对象一旦被创建,在后期的调用过程中就不会进入同步的代码,因此,有更高的效率。