JAVA基础
- T1.为什么要使用克隆?
- T2.如何实现对象克隆?
- T3.深拷贝和浅拷贝的区别是什么?
- 1.JDK和JRE有什么区别?
- 2. ==和equals的区别是什么?
- 3.两个对象的hashCode()相同,则equals()也一定为true,这句话对吗?
- 4.final在Java中有什么作用?
- 5.Math.round(-1.5)等于多少?
- 6.String属于基础的数据类型吗?
- 7.JAVA中操作字符串的类有哪些?它们之间有什么区别?
- 8.String str="i"与String str=new String("i")一样吗?
- 9.如何将字符串反转?
- 10.String类的常用方法都有哪些?
- 11 .抽象类必须要有抽象方法吗?
- 12 .普通类和抽象类有什么区别?
- 13.抽象类可以使用final修饰吗?
- 14.接口和抽象类有哪些区别?
- 15.何时使用接口,何时使用抽象类?
- 16.JAVA中IO流分为几种?
- 17.File的常用方法有哪些?
- 18.BIO、NIO、AIO的区别?
- 19.final、finally、finalize的区别?
- 20.方法重写和和方法重载的区别?
- 21.JAVA支持多继承吗?
- 22.什么是构造函数?什么是构造函数重载?
- 23.修饰符public、protected、默认不写、private的作用域?
- 24.堆内存和栈内存的区别?
- 25.值传递和引用传递的区别?
- 26.什么是自动装箱,什么是自动拆箱?
- 27.什么是.class文件?
- 28.异常处理的原则是什么?
- 29.static随着类的加载而加载
- 30.下图中分别包含几个对象?
- 31.为什么说char数组比String更适合存储密码?
- 32. instanceof关键字怎么使用?
- 33.URL和URI的区别?
- 34.创建对象之间的比较
- 35.自增运算
- 36.关键字总结
- 37.一个类什么时候不能有子类除了被final修饰的情况?
- 38. / 和 ./ 和 2点 / 的区别?
- 39.JPA是什么?有什么用?
- 40.JAVA中各种O代表什么意思?
- 41.Java基本数据类型各占用几个字节?
- 42.类的加载顺序
- 43.BigDecimal数据类型?
- 44.将对象添加到集合里,实现多属性排序
- 45.堆内存和栈内存的区别?
- 46.将Properties文件持久到磁盘
- 47.java中按位运算和移位运算
- 48.深拷贝和浅拷贝
- 49.强引用,软引用,弱引用,虚引用
- 50.NAS盘(跨平台文件共享服务器)
- 51.File.separator 和 Line.separator
- 52.接口中被default修饰的方法
- 53.使用sonar进行代码质量检查。
- 54.Http的长连接和短连接
T1.为什么要使用克隆?
- 想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了,Java语言中的克隆针对的是类的实例。
T2.如何实现对象克隆?
有以下两种方法:
- 1.实现Cloneable接口并重写Object的clone方法。
- 2.实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的克隆,代码如下:
package com.springCloud.consumer.controller;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import java.io.*;
/**
* 克隆工具类
*/
public class MyUtil {
private MyUtil(){
throw new AssertionError();
}
@SuppressWarnings("unchecked")
//此注解告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。
public static <T extends Serializable> T clone(T obj) throws Exception{
ByteArrayOutputStream baos =new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (T) ois.readObject();
// 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
}
}
/*Person类*/
@Data
@AllArgsConstructor
@ToString
class Person implements Serializable{
private static final long serialVersionUID = -9102017020286042305L;
private String name;
private int age;
private Car car;
}
/*Car类*/
@Data
@AllArgsConstructor
@ToString
class Car implements Serializable{
private static final long serialVersionUID = -5713945027627603702L;
private String brand;
private int maxSpeed;
}
/**
* 克隆测试类
*/
class CloneTest{
public static void main(String[] args) {
Person p1 = new Person("张三",22,new Car("Benz",300));
try {
Person p2 = MyUtil.clone(p1);
p2.getCar().setBrand("BYD");
p2.getCar().setMaxSpeed(180);
System.out.println("p1 = " + p1);
System.out.println("----------");
System.out.println("p2 =" + p2);
/*修改克隆的Person对象p2关联的汽车对象的品牌属性
原来的Person对象p1关联的汽车不会受到任何影响
因为在克隆Person对象时其关联的汽车对象也被克隆了*/
} catch (Exception e) {
e.printStackTrace();
}
}
/*输出结果:
p1 = Person(name=张三, age=22, car=Car(brand=Benz, maxSpeed=300))
----------
p2 =Person(name=张三, age=22, car=Car(brand=BYD, maxSpeed=180))*/
}
T3.深拷贝和浅拷贝的区别是什么?
- 浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中一个对象的某个值,另外一个对象的对应值都会随之变化,这就是浅拷贝(如clone方法)。
- 深拷贝是将对象和值复制过来,修改其中一个对象的某个值,另外一个对象的对应值不会随之改变,这就是深拷贝(如对象输入输出流)。
1.JDK和JRE有什么区别?
- JDK: JAVA Development Kit的简称,JAVA开发工具包,包括了JAVA的开发环境和运行环境(JRE)。
- JRE: JAVA Runtime Environment的简称,JAVA运行环境。
- 两者关系: JDK包含JRE,同时还包含了JAVA源码编译器 JAVAC,还包含了JAVA程序调试和分析的工具。
2. ==和equals的区别是什么?
- ==解读
- 对于基本数据类型: 比较的是值是否相等。
- 对于引用数据类型: 比较的是内存中的地址值。
String x = "String";//字符串常量池
String y = "String";//字符串常量池
String z = new String("String");
System.out.println(x==y);// true
System.out.println(x==z);// false
System.out.println(x.equals(y));// true
System.out.println(x.equals(z));// true
- equas解读
- equals本质上就是==,只不过String和Integer等重写了equals方法,把他变成了值比较.
//Cat类是自定义的一个类(Cat类中没有重写equals方法)
Cat c1 = new Cat("猫");
Cat c2 = new Cat("猫");
System.out.println(c1.equals(c2));//结果为false
/*因为Cat类中没有重写equals方法,
那么调用的是object的equals方法,因为是引用类型,
所以比较的是内存中的地址值,所以结果为false*/
3.两个对象的hashCode()相同,则equals()也一定为true,这句话对吗?
- 答案是不对的
- JAVA代码示例:
String str1 = "通话";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d", str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));
//执行结果1: str1:1179395 | str2:1179395
//执行结果2: false
/*显然 “通话” 和 “重地” 的 hashCode() 相同,
然而 equals() 则为 false*/
4.final在Java中有什么作用?
- final修饰的变量: 是常量,必须初始化值。
- final修饰的方法: 是最终方法,不能被子类重写。
- final修饰的类: 是最终类,不能被继承。
5.Math.round(-1.5)等于多少?
- 等于-1,因为在数轴上取值时,中间值(0.5)向右取整。
6.String属于基础的数据类型吗?
- String 属于引用数据类型
- 基本数据类型:
byte,short,int,long ,float,double,char,boolean - char占2字节(16位)可存放汉字:char a=‘中’
7.JAVA中操作字符串的类有哪些?它们之间有什么区别?
- String 、StringBuffer、 StringBuilder
- 1 .String 字符串常量(JAVA内的对象,是不可变的对象)
- 2.StringBuffer 字符串变量(线程安全,用在多线程)
- 3.StringBuilder 字符串变量(非线程安全,用在单线程)
8.String str="i"与String str=new String(“i”)一样吗?
- 不一样:
前者中的"i"是在字符串常量池中,后者中的"i"是被分配到堆内存中
9.如何将字符串反转?
- 调用StringBuffer或者StringBuilder的reverse( ) 方法。
10.String类的常用方法都有哪些?
- 1.indexOf(): 返回指定字符的索引。
- 2.charAt(): 返回指定索引处的字符。
- 3.replace(): 字符串替换。
- 4.trim(): 去除字符串两端的空白。
- 5.split(): 分割字符串,返回一个分割后的字符串数组。
- 6.getBytes(): 返回字符串的字节数组。
- 7.length(): 返回字符串长度。
- 8.toLowerCase(): 将字符串转换成小写字母。
- 9.toUpperCase(): 将字符串转换成大写字母。
- 10.substring(): 截取字符串。
- 11.equals():比较字符的值。
11 .抽象类必须要有抽象方法吗?
- 抽象类可以没有抽象方法
12 .普通类和抽象类有什么区别?
- 1.抽象方法 普通类不能有,抽象类可以有。
- 2.实例化 普通类可以被实例化,抽象类不能直接被实例化。
13.抽象类可以使用final修饰吗?
- 不能,被final修饰的类表示最终类,不能被继承,而定义抽象类就是让其他类继承的,因此产生矛盾。
14.接口和抽象类有哪些区别?
- 1.实现:一个类可以实现多个接口,但只能继承一个抽象类。
- 2.构造函数:抽象类可以有,但接口不能有。
- 3.主函数(main方法):抽象类可以有,接口不能有。
- 4.方法修饰符:接口中的方法默认被public abstract修饰。抽象类中修饰符可以任意的。
- 5.普通成员变量:抽象类中可以有,接口中没有(接口中只有公共静态常量)。
- 6.抽象方法:接口必须要有,抽象类可以没有。
15.何时使用接口,何时使用抽象类?
- 接口: 主要用于抽象功能
- 抽象类: 主要用于抽象类别
16.JAVA中IO流分为几种?
- 1.按流向来分:输入流和输出流。
输入流的基础类:inputStream和Reader
输出流的基础类:OutputStream和Writer - 2.按处理的内容来分:字节流和字符流。
字节流的常用基础类:inputStream和OutputStream
字符流的常用基础类:Reader和Writer - 3.字节流和字符流的区别
字节流操作数据的基本单位:8位的字节
字符流操作数据的基本单位:16位的字节
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileOutputStream fos = new FileOutputStream("D:\\test\\a.txt",true);//append拼接 不覆盖
fos.write("hello 你好".getBytes(StandardCharsets.UTF_8));
fos.write("\r\n".getBytes());// \r\n换行
fos.write("hello 蛮王".getBytes(StandardCharsets.UTF_8));
fos.close();
//字符输入 输出流 实现文件复制
FileReader fr = new FileReader("D:\\test\\a.txt");
FileWriter fw = new FileWriter("D:\\test\\c.txt");
int len2 = 0;//读取的有效个数
char [] buf = new char[50];//buf 缓冲区 容器
while ((len2=fr.read(buf))!=-1){
// System.out.println("buf = " + new String(buf,0,len2));
fw.write(buf,0,len2);
}
fr.close();
fw.close();
//字节输入 输出流实现文件复制
FileInputStream fis = new FileInputStream("D:\\test\\a.txt");
FileOutputStream fos2 = new FileOutputStream("D:\\test\\b.txt");
int len = 0;//记录有效个数
byte[] bytes = new byte[50];//缓冲区,每次读50个字节
while ((len=fis.read(bytes))!=-1){
//System.out.println("new String(bytes,0,i) = " + new String(bytes,0,len));
fos2.write(bytes,0,len);
}
fis.close();
fos2.close();
//使用对象流 实现对象深拷贝
MyConfig config = new MyConfig();
config.setCode(200);
config.setPassword("23123");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(config);
ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
MyConfig config1 = (MyConfig)ois.readObject();
System.out.println("config1 = " + config1);
System.out.println(config==config1);//false
}
17.File的常用方法有哪些?
- 判断
1.exists():判断文件、文件夹是否存在
2.isFile():判断是否是文件
3.isDirectory():判断是否是文件夹
4.canRead():判断是否可读
5.canWrite():判断是否可写
6.canExecute():判断是否可执行
7.isHidden():判断是否隐藏 - 获取
1.getAbsolutePath():获取绝对路径
2.getFreeSpace():获取空余空间,单位是字节
3.getName():获取文件名
4.getParent():获取父目录的路径
5.getPath():获取文件对象定义指向的路径
6.getTotalSpace():获取所有空间大小,单位是字节
7.getUsableSpace():获取jvm的可用空间,单位是字节
8.getAbsoluteFile():获取绝对路径所创建的文件对象
9.length():获取文件大小
10.list():返回指定目录下的所有文件名组成的数组
11.listFile():返回指定目录下所有文件对象组成的数组
18.BIO、NIO、AIO的区别?
- 1.BIO:Block IO的简写,同步阻塞式IO(传统IO)
特点: 模式简单,使用方便,并发处理能力低。 - 2.NIO:New IO的简写,同步非阻塞IO(传统IO的升级)
特点: 客户端和服务端通过Channel(通道)通讯,实现了多路复用。 - 3.AIO:Asynchronous IO的简写,异步非阻塞IO(NIO的升级)
特点: 异步IO的操作基于事件和回调机制
19.final、finally、finalize的区别?
- final: 用于修饰属性,方法和类,分别表示属性不可变,方法不能被重写,类不能被继承。
- finally: 是异常处理语句结构的一部分,表示总是执行。
- finalize(): 是Object类的一个方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。
- GC: Garbage Collection的简写,垃圾收集。
20.方法重写和和方法重载的区别?
- 方法重载(overloading)
多个同名方法同时存在于一个类中,参数列表各不相同(类型,个数) - 方法重写(overriding)
子类通过对父类方法的重写,进行功能增强,其方法名和参数列表和父类相同。
注释:@overriding(保证重写方法的上一行是注释)
21.JAVA支持多继承吗?
- JAVA中的类: 不支持多继承,只支持单继承。
- JAVA中的接口: 支持多继承
子接口继承多个父接口,说明子接口扩展了多个功能.
22.什么是构造函数?什么是构造函数重载?
- 构造函数: 创建对象时,构造函数会被调用,函数名与类名相同。
- 构造函数重载: 一个类中存在多个构造函数,参数列表各不相同。
23.修饰符public、protected、默认不写、private的作用域?
- public: 同包和其他包都可以使用。
- protected: 同包和其他包的子孙类可以使用。
- 默认不写: 只有同包内可以使用。
- private: 只有本类中才可以使用。
24.堆内存和栈内存的区别?
- ①堆内存
堆内存用来存放所有new 创建的对象和数组的数据 - ②栈内存
栈内存主要存放的是 基本数据类型
(int,short,long,byte,float,boolean,char)和对象句柄(例:String s 就是对象句柄);
注意:并没有String基本类型,在栈内存的数据大小,生存周期必须是确定的。
优点:寄存速度快,栈数据可以共享。
缺点:数据固定,不够灵活。
(当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,
该内存空间可以立刻被另作他用。)
25.值传递和引用传递的区别?
- 值传递:是对于基本类型变量而言的,传递的是该变量的一个副本,改变副 本不改变原变量;
- 引用传递:是对于对象型变量而言的,传递的是该对象在内存中的地址值,对引用对象操作会改变原对象。
26.什么是自动装箱,什么是自动拆箱?
- **自动装箱:**就是自动将 基本数据类型转换为包装器类型;
- **自动拆箱:**就是自动将包装器类型转换为基本数据类型。
① Integer i = 8; (自动装箱)
相当于自动创建一个Integer对象:Integer i = new Integer(8);(Integer就是int的包装器类)
② int t = i; (自动拆箱)
27.什么是.class文件?
- ①JAVAC编译器将原有的.java文件翻译成二进制的字节码存在.class文件中。
- ②而且class文件是跨平台的基础,有了class文件,JVM才能加载java类
28.异常处理的原则是什么?
- ①在通用方法里面,不要捕获异常,而是直接抛出异常,让调用层处理。
- ②用户访问界面处理掉所有可能出现的异常,并记录详细错误日志,然后返回友好的错误界面给用户。
29.static随着类的加载而加载
- static 变量,static 方法,static代码块 。
①随着类的加载而加载,优先于对象存在,不依赖类的实例,被所有对象共享–static的成员变量和方法可以直接被类名调用。
②static的方法仅能调用其他的static方法,只能访问static数据,不能以任何方式引用this或者super。
③通常一个普通类不允许声明为 静态(是非法),只有内部类才允许。内部类被static修饰变为静态内部类。
④静态代码块:在类加载时优先加载,而且只执行一次.
30.下图中分别包含几个对象?
- ① 1个对象,在栈内存中。
- ② 2个对象,左边在栈内存中,右边在堆内存中
- ③ 3个对象,多了一个字符串常量池的对象
31.为什么说char数组比String更适合存储密码?
- String是字符串常量类,是不可变的
- char数组是可变的,可以被覆盖
//1.假设password是前端提交过来的密码
String password = "123";
//2.假设在后台中,对密码进行处理
String password = "xxxxxs"
//3.原来的"123"依旧在内存中,所以相当不安全
32. instanceof关键字怎么使用?
- instanceof:测试一个对象是否为另一个类的实例
//Class代表某个类或者接口
boolean b = obj instanceof Class;
- ①obj必须为引用类型
- ②obj可以为null
- ③obj可以为Class类的实例对象
- ④obj可以为Class接口的实现类
- ⑤obj可以为Class类的子类
public static void main(String[] args) {
Integer integer = new Integer(2);
System.out.println(integer instanceof Object);//ture
boolean b = integer instanceof Object;
System.out.println("b = " + b);//true
int i = 2;
//System.out.println(i instanceof Object) 编译错误
}
33.URL和URI的区别?
- URI 统一资源标志符:标记了一个网络资源
- URL 统一资源定位符:标记了一个网络资源,并给出了一个访问地址
- URN 统一资源名称:通过特定命名空间的唯一名称或ID来表示资源
- 三者关系:URI包括URN和URL,URL是URI的子集,所以URL一定 URI,但URI不一定是URL
34.创建对象之间的比较
35.自增运算
//求k的值
int i = 1;
int j = 2;
int k = i++ + ++j + i++;
- 自增运算符的优先级比算数运算符的优先级要高。
- i++的结果是1,算完以后i的值是2。
- ++j的结果是3,算完以后j的值是3。
- i++的结果是2(因为在此之前i的值在第一个i++完成后变成了2),算完以后i的值是3。所以k的值就是1+3+2=6。
36.关键字总结
-
break
- ①结束循环,跳出循环体,进行后面的程序;
- ②switch case结构中结束选择分支
-
continue
- ①结束本次循环,进行下次循环;
-
return
- ①跳出循环体所在的方法,相当于结束该方法;
- ②return +[返回类型的对象] 传给需要返回的方法
-
super
- ①子类在构造方法中显示调用父类构造方法
- ②在子类内部代表父类对象,从而在子类中访问父类的属性和方法。
-
this
- ①在类的内部永远代表正在执行此代码的对象;在成员方法和构造方法内部,都可以通过this来明确访问类中的成员属性和成员方法。
- ②在构造方法内部通过this(参数列表),可以调用其他构造方法。
37.一个类什么时候不能有子类除了被final修饰的情况?
- 该类私有化的构造函数,那么继承该类的子类中的构造函数就无法调用该类的构造器。子类默认调用父类的构造器super()。
class A{
private A(){
}
}
class B extends A{
B(){
super();//报错
}
38. / 和 ./ 和 2点 / 的区别?
/ : 当前文件所在的根目录
./ : 当前文件所在的当前目录
../ : 当前文件的上级目录
<!--假设在user.html中引用jquery.min.js静态资源,user.html就代表当前文件 -->
<script src="./public/libs/jquery.min.js"></script>
39.JPA是什么?有什么用?
- JPA:是java Persistence API的简写,中文名:java持久层API,是JDK5.0注解描述对象和数据库表之间的映射关系,JPA框架根据这个关系把实体对象持久化到数据库表。
40.JAVA中各种O代表什么意思?
- ① PO :persistant object 持久对象,持久到数据库表的对象,用在DAO层。
- ② DO:Domain Object 域对象。
- ③ DTO:Data Transfer Object 数据传输对象。数据和对象之间的转换。
- ④ VO:view object 视图对象,包含在前端显示的数据,一般用在web层。
- ⑤ BO:business object 业务对象,一般用在service层。
- ⑥ POJO:plain ordinary java object 简单无规则 java 对象。
- ⑦ DAO:data access object 数据库访问对象。
- ⑧ QO:query object 查询对象,用于封装前端发送过来的查询信息。
- ⑨ entity:实体对象。
41.Java基本数据类型各占用几个字节?
42.类的加载顺序
1. 父类静态成员变量,父类静态代码块(谁放前面就先加载谁)
2. 子类静态成员变量,子类静态代码块(谁放前面就先加载谁)
3. 父类普通成员变量
4. 父类非静态代码块
5. 父类构造函数
6. 子类普通成员变量
7. 子类非静态代码块
8. 子类构造函数
class A{
private F f =new F();
private static Fj fj = new Fj();
static {
System.out.println("父类静态代码块");
}
{
System.out.println("父类非静态代码块");
}
A(){
System.out.println("父类构造函数");
}
public static void test(){
System.out.println("父类静态方法");
}
}
class B extends A{
private Z z =new Z();
private static Zj zj= new Zj();
static {
System.out.println("子类静态代码块");
}
{
System.out.println("子类非静态代码块");
}
B(){
System.out.println("子类构造函数");
}
public static void test(){
System.out.println("子类静态方法");
}
//测试 new一个子类对象。。。。
public static void main(String[] args) {
B b = new B();
父类静态成员变量
父类静态代码块
子类静态成员变量
子类静态代码块
父类普通成员变量
父类非静态代码块
父类构造函数
子类普通成员变量
子类非静态代码块
子类构造函数
}
}
class F{
F(){
System.out.println("父类普通成员变量" );
}
}
class Z{
Z(){
System.out.println("子类普通成员变量" );
}
}
class Fj{
Fj(){
System.out.println("父类静态成员变量" );
}
}
class Zj{
Zj(){
System.out.println("子类静态成员变量" );
}
}
43.BigDecimal数据类型?
//初始化
BigDecimal n1 = new BigDecimal("123456.12345678901");
BigDecimal n2 = new BigDecimal("1.0");
// ++++
BigDecimal add = n1.add(n2);
System.out.println("add = " + add);
// ----
BigDecimal subtract = n1.subtract(n2);
System.out.println("subtract = " + subtract);
// multiplicand:被乘数 MathContext: 包含精度(保留数字个数)和舍入模式
BigDecimal multiply = n1.multiply(n2, MathContext.DECIMAL32);
System.out.println("multiply = " + multiply);
//参数说明:divisor 除数,scale 小数点后面保留的位数,roundingMode 舍入模式
//ROUND_HALF_UP :四舍五入
BigDecimal divide = n1.divide(n2, 6, BigDecimal.ROUND_HALF_UP);
System.out.println("divide = " + divide);
44.将对象添加到集合里,实现多属性排序
- 方式一:实现Comparator接口,重写compare方法。
@Data
@AllArgsConstructor
@NoArgsConstructor
class Student implements Comparator<Student> {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return name.equals(student.name) &&
num.equals(student.num) &&
Objects.equals(age, student.age) &&
score.equals(student.score);
}
@Override
public int hashCode() {
return Objects.hash(name, num, age, score);
}
private String name;
private Integer num;
private Integer age;
private Integer score;
@Override
public int compare(Student o1, Student o2) {
int num = o1.num - o2.num;
if (num==0){
int age = o2.age - o1.age;
if (age==0){
return o1.score-o2.score;
}else {
return age;
}
}else {
return num;
}
}
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("zs", 1, 20, 55),
new Student("ls", 3, 25, 33),
new Student("ww", 2, 34, 62),
new Student("zl", 5, 33, 65),
new Student("xq", 5, 22, 52),
new Student("wb", 5, 12, 77),
new Student("xq", 5, 30, 99),
new Student("xs", 5, 30, 88),
new Student("sy", 5, 30, 33),
new Student("se", 4, 12, 88));
students.sort(new Student());
students.forEach(System.out::println);
}
}
- 方式二:使用Comparator的comparing方法
@Data
@AllArgsConstructor
@NoArgsConstructor
class Student {
private String name;
private Integer num;
private Integer age;
private Integer score;
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("zs", 1, 20, 55),
new Student("ls", 3, 25, 33),
new Student("ww", 2, 34, 62),
new Student("zl", 5, 33, 65),
new Student("xq", 5, 22, 52),
new Student("wb", 5, 12, 77),
new Student("xq", 5, 30, 99),
new Student("xs", 5, 30, 88),
new Student("sy", 5, 30, 33),
new Student("se", 4, 12, 88));
students.sort(Comparator.comparing(Student::getNum)
.thenComparing(Student::getAge, (s1, s2) -> s2-s1)
.thenComparing(Student::getScore));
students.forEach(System.out::println);
//=========================倒叙比较器的使用,单属性反转顺序========================
students.sort(Comparator.comparing(Student::getNum)
//Comparator.reverseOrder()返回一个倒叙比较器
//相当于.thenComparing (Student::getAge, (s1, s2) -> s2-s1)
.thenComparing(Student::getAge,Comparator.reverseOrder())
.thenComparing(Student::getScore,Comparator.reverseOrder()));
students.forEach(System.out::println);
//==============reversed()方法可以使前面的顺序全部倒过来====================
/*students.sort(Comparator.comparing (Student::getNum)
.thenComparing (Student::getAge, (s1, s2) -> s2-s1)
.thenComparing (Student::getScore).reversed());
students.forEach(System.out::println);*/
}
}
45.堆内存和栈内存的区别?
46.将Properties文件持久到磁盘
//将properties文件持久到硬盘
Properties properties = new Properties();
properties.setProperty("k1", "zs");
properties.setProperty("k2", "ls");
properties.setProperty("k3", "ww");
//java程序中拼接路径 windows和linux兼容方式
String rPath = "D:"+File.separator+"tst"+File.separator+"aba"+File.separator+"xxx.properties";
File file = new File(rPath);
if (!file.exists())
{
//先创建文件所在的目录
//noinspection ResultOfMethodCallIgnored
file.getParentFile().mkdirs();
//再创建新文件
//noinspection ResultOfMethodCallIgnored
file.createNewFile();
}
FileWriter writer = new FileWriter(rPath);
//第二个参数 相当于在properties文件中打个注释 #save data
properties.store(writer,"save data");
47.java中按位运算和移位运算
- 按位与 &:同为1 则为1
//0000 0011
//0000 0101
//0000 0001
int a = 3 & 5;//1
- 按位或 | : 有1即为1
//0000 0011
//0000 0101
//0000 0111
int b = 3 | 5; //7
- 按位非 ~ :对该整数的二进制形式 逐位取反
//0000 0101 ==> 1111 1010
int d = ~5;
- 按位异或^ :相同为0,不同为1
//0000 0011
//0000 0101
//0000 0110
int c = 3 ^ 5;
- 计算机对有符号数(包括浮点数)的表示有三种方法:原码、反码和补码, 补码=反码+1
- 在 二进制里,是用 0 和 1 来表示正负的,最高位为符号位,最高位为 1 代表负数,最高位为 0 代表正数
- 对于负数而言,是以补码的形式存储在内存中的
- 负整数转二进制:以-8为例子
第一步:将-8的绝对值转化为二进制 0000 1000
第二步:将上面的二进制以反码表示 1111 0111
第三步: 转化为补码 = 反码加1 1111 1000
- 二进制转负整数
二进制数 1111 1000
对二进制取反 0000 0111
加1 0000 1000
转化为整数并带上负号 -8
- 左移<<: m<<n的含义:把整数m表示的二进制数左移n位,高位移出n位都舍弃,低位补0
// 3<<2剖析:
3二进制形式: 00000000 00000000 00000000 00000011,
进行左移 00000000 00000000 00000000 00001100,即为12.
- 右移>>: m>>n的含义:把整数m表示的二进制数右移n位,m为正数,高位全部补0;m为负数,高位全部补1. (算术右移 有符号右移)
// 3>>2剖析:
3二进制形式: 00000000 00000000 00000000 00000011
右移 00000000 00000000 00000000 00000000,即为0.
- 无符号右移 >>>: m>>>n:整数m表示的二进制右移n位,不论正负数,高位都补零。 (逻辑右移 无符号右移)
// 3>>>2剖析:
3二进制形式: 00000000 00000000 00000000 00000011
无符号右移 00000000 00000000 00000000 00000000,即为0.
48.深拷贝和浅拷贝
@Data
class A implements Cloneable,Serializable{
private static final long serialVersionUID = 600530931242383778L;
private B b;
private String name;
@Override
public Object clone() throws CloneNotSupportedException {
A clone = (A)super.clone();
clone.setB((B) b.clone());
return clone;
}
}
@Data
class B implements Cloneable,Serializable{
private static final long serialVersionUID = -5132914324293905073L;
private String bname;
private Integer bage;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Test{
public static void main(String[] args) throws Exception {
//TODO 源对象
A a = new A(); a.setName("xxx");
B b = new B(); b.setBname("bbb");
a.setB(b);
A clone = (A)a.clone();
//TODO 利用==来比较对象类型数据在内存中的地址值
System.out.println("1."+( a==clone ));//false,调用clone方法相当于重新new一个
System.out.println("2."+( clone.getB()==b ));//false 因为A的clone方法中调用了b的clone方法
System.out.println("3."+( clone.getB().getBname()==a.getB().getBname() )); //true,复制的是String的内存地址值
System.out.println("4."+( clone.getName()==a.getName() )); //true ,复制的是String的内存地址值
System.out.println("============================================");
//TODO Spring的 BeanUtils也是浅拷贝,是内存中地址值的传递
A copy = new A();
BeanUtils.copyProperties(a,copy);
System.out.println("5."+( clone.getName()==a.getName() ));//true
System.out.println("6."+( copy.getB() == a.getB() ));//true
System.out.println("7."+( copy.getB().getBname() == a.getB().getBname() ));//true
System.out.println("============================================");
//TODO:对象流 是深拷贝 涉及到的对象类型属性全都是new出来的 而且属性值是和源对象一样的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(a);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
A readObject = (A) ois.readObject();
System.out.println("8."+( readObject.getName() == a.getName() ));//false
System.out.println("9."+( readObject.getB() == a.getB() ));//false
System.out.println("10."+( readObject.getB().getBname() == a.getB().getBname() ));//false
oos.close();
ois.close();
}
}
49.强引用,软引用,弱引用,虚引用
-
1 强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。
-
2 软引用:内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。通过 SoftReference 实现软引用。
-
3 弱引用:如果一个对象具有弱引用,在垃圾回收时候,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。我们的WeakHashMap是基于弱引用的,也就是说只要垃圾回收机制一开启,就直接开始了扫荡,看见了就清除。通过 WeakReference 实现弱引用。
-
4 虚引用:如果一个对象具有虚引用,就相当于没有引用,在任何时候都有可能被回收。使用虚引用的目的就是为了得知对象被GC的时机,所以可以利用虚引用来进行销毁前的一些操作,比如说资源释放等。通过 PhantomReference 实现虚引用。
//TODO 强引用
String str = "hello" ;
Object object = new Object();
//TODO SoftReference<T> 软引用
SoftReference<String> stringSoftReference = new SoftReference<>("xxx");
String s2 = stringSoftReference.get();
WSResponse wsResponse = new WSResponse();
wsResponse.setCode("122335555");
//TODO PhantomReference<T> 弱引用
WeakReference<WSResponse> stringWeakReference = new WeakReference<WSResponse>(wsResponse);
WSResponse s = stringWeakReference.get();
System.out.println("s = " + s);
System.gc();
WSResponse s1 = stringWeakReference.get();
System.out.println("s1 = " + s1);
//TODO PhantomReference<T> 虚引用
PhantomReference<String> phantomReference = new PhantomReference<>("ggg",null);
String s3 = phantomReference.get()
50.NAS盘(跨平台文件共享服务器)
- Network Attached Storage 网络附加存储
网络附加存储作为一种专用的网络文件服务器,跟其他的服务器一样需要有操作系统的支持。
网络存储基于标准网络协议实现数据传输,为网络中的Windows / Linux / Mac OS 等各种不同操作系统的计算机提供文件共享和数据备份。
由于NAS操作系统支持多个操作系统,为此其跨平台的性能就相当的不错。其不仅可以支持Windows客户端,而且还可以支持Linux等客户端系统。如现在不少的外资企业和银行等金融企业,采用的大部分都是基于Linux核心的客户端系统,此时如果采用NAS网络附加存储方案,那么就可以获得比较好的兼容性。如在中文的支持上都会有很好的体现。
它允许用户使用各种终端设备以各种网络服务形式来访问存储服务器上的资料。
51.File.separator 和 Line.separator
- File.separator 解决Linux和Windows系统下路径分隔符兼容问题。
- Line.separator 解决Linunx和Windows系统下换行符兼容问题。
52.接口中被default修饰的方法
- 该接口实现类可以继承该方法,可由实现类的对象调用。
53.使用sonar进行代码质量检查。
54.Http的长连接和短连接
- 长连接:客户端与服务器方先建立连接,连接建立后不断开,然后再进行报文发送和接收。这种方式下由于通讯连接一直存在。此种方式常用于P2P通信。
短连接:客户端方与服务器每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。此方式常用于一点对多点通讯。C/S通信。 - 短连接的操作步骤:
建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接 - 长连接的操作步骤:
建立连接——数据传输…(保持连接)…数据传输——关闭连接 - 长连接与短连接的使用时机
短连接多用于操作频繁,点对点的通讯,而且连接数不能太多的情况。每个TCP连接的建立都需要三次握手,每个TCP连接的断开要四次握手。如果每次操作都要建立连接然后再操作的话处理速度会降低,所以每次操作后,下次操作时直接发送数据就可以了,不用再建立TCP连接。例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,频繁的socket创建也是对资源的浪费。
Web网站的http服务一般都用短连接,因为长连接对于服务器来说要耗费一定的资源。像web网站这么频繁的成千上万甚至上亿客户端的连接用短连接更省一些资源。试想如果都用长连接,而且同时用成千上万的用户,每个用户都占有一个连接的话,可想而知服务器的压力有多大。所以并发量大,但是每个用户又不需频繁操作的情况下需要短连接。
总之:长连接和短连接的选择要根据需求而定。
长连接和短连接的产生在于client和server采取的关闭策略,具体的应用场景采用具体的策略,没有十全十美的选择,只有合适的选择。 - HTTP协议长连接、短连接总结
长连接与短连接的不同主要在于client和server采取的关闭策略不同。短连接在建立连接以后只进行一次数据传输就关闭连接,而长连接在建立连接以后会进行多次数据数据传输直至关闭连接(长连接中关闭连接通过Connection:closed头部字段)。 - 长连接的优点 :
1 通过开启、关闭更少的TCP连接,节约CPU时间和内存
2.通过减少TCP开启引起的包的数目,降低网络阻塞。 - 二者所应用的具体场景不同。短连接多用于操作频繁、点对点的通讯,且连接数不能太多的情况。数据库的连接则采用长连接.