一个Java文件中能否可以定义多个类
一个Java源文件里可以定义多个Java类,但最多只能有一个类被public修饰。若源文件中包括了public类,源文件必须和该public类同名
构造器能否可被
重写或
重载
构造器 Constructor、重写 Override、重载 Overload
首先了解构造器
- 构造器名称和类名相同,没有返回类型,不能使用return语句
- 其作用是创建对象,初始化对象
- 生成类的对象时自动执行,无需调用
由此可得知构造器不能被重写,且没必要继承其构造器,因为当实例化子类时,底层会首先实例化父类
构造器能够被重载
构造器有无参构造器和有参构造器之分,而有参构造器的参数可以不同
重载和重写的区别
方法重载:
在同一个类中,方法名相同的,方法参数不同(参数类型、参数个数、参数顺序)
方法重载的作用:屏蔽了相同功能的方法由于参数不同所造成方法名称不同。
方法重载和方法的返回值类型无关,只是一般要求返回值类型相同。
方法重写:
重写是子类对从父类继承到的方法进一步扩展或者重新编写
- 方法名,方法参数相同
- 返回值范围小于等于父类方法的返回值类型
- 访问修饰符范围大于等于父类
- 抛出的异常类型小于等于父类
若父类方法被private访问修饰符修饰,则子类不能重写该方法
Java 面向对象编程三大特性
- 封装
- 继承
- 多态
说说List,Set,Map三者的区别?
- List:允许元素重复,允许纪录元素的添加顺序
- Set: 不允许元素重复,不允许纪录元素的添加顺序
- Map: 使用键值对存储。Key相当于Set集合,Value相当于List集合
String 为什么是不可变的
String 类中使用 final 关键字 修饰 字节数组 来保存字符串,所以 String 对象是不可变的
private final byte[] value;
java 8 之前使用字符数组保存,java 9 之后用字节数组保存,节省空间
String、StringBuffer 和 StringBuilder 的区别是什么
可变性
String 类中使用 final 关键字 修饰 字节数组 来保存字符串,所以 String 对象是不可变的
private final byte[] value;
而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder中也是使用字节数组 保存字符串 byte[] value
但是没有用 final关键字修饰,所以这两种对象都是可变的
byte[] value;
线程安全性
StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以可以保证线程安全。
而StringBuilder 并没有对方法加同步锁,所以不能保证线程安全。
性能
每次对String字符串类型进行修改的时候,都会生成一个新的String对象,然后将指针指向新的 String 对象。若进行大量的字符串修改操作时,会造成内存空间的浪费
StringBuffer ,StringBuffer 每次都会对对象本身进行操作,而不是生成新的对象并改变对象引用。
在执行操作数量不大的情况下(10W次拼接),两者性能差不多,当操作次数达到百万级别千万级别甚至更多时,使用 StringBuilder 相比使用 StringBuffer 能够获得 10% ~ 15% 左右的性能提升,但却要冒多线程不安全的风险。
使用场景
- 操作少量的数据: 适用 String
- 单线程中操作大量字符串数据修改时: 适用 StringBuilder
- 多线程中操作大量字符串数据修改时: 适用 StringBuffer
扩展
Java8 之前String底层存储字符串最小单位是一个char,用到两个byte,但是 Java8 是基于 latin1 (ISO-8859-1) 的,而这个latin1编码可以用一个byte标识。当你数据明明可以用到一个byte存储时,我们却用了一个最小单位char(两个byte),这时就多浪费了一个byte空间。
Java9 在这一方面进行了更新,现在的 Java9 是基于latin1/Utf-16 。
latin1 用 一个byte 标识,UTF-16 用 两个byte 标识
Java9 会自动识别用哪个编码,当数据只需用到1个byte时就会使用latin1 ,当数据需要用到2个byte时就会使用utf-16
这样便会节省了许多不必要的内存开销
自动装箱与拆箱
装箱 :将基本数据类型用对应的引用类型包装起来;
拆箱 :将包装类型转换为基本数据类型
接口和抽象类的区别是什么
相同点
- 都不能实例化
区别
- 接口方法默认是public,abstract,且只能被这两个关键字修饰,方法不能被实现 (Java 8 开始接口方法可以有默认实现 default,静态实现 static)
- 抽象类可以同时定义抽象和非抽象方法,抽象方法可以有 public、protected 和 default 这些修饰符(抽象方法的存在就是为了被重写,所以不能使用 private 关键字修饰)
- 接口中的成员变量,默认时是public static final,且只能够是静态不能被修改的,而且必须给其初始化赋值,而抽象类则不一定
- 一个类可以实现多个接口,但只能继承一个抽象类。而且接口也可以继承多个接口类来扩展功能
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
帮助子类做初始化工作
== 与 equals区别
基本数据类型 " == " 是比较值
引用数据类型 " == " 和 equals() 两者都是 比较两个对象的引用地址
大多情况下我们关心的是数据而不是内存地址
所以我们一般重写 equals() 方法来比较对象的类型及内容
&和&&的区别
&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,返回true,若只要有一方为false,则结果为false。
&&还具有短路的功能,如果第一个表达式为false,则不再计算第二个表达式,减少一次判断次数
final 关键字修饰这三个地方:变量、方法、类,会有什么作用
- final修饰的变量:表示常量,该变量一旦被初始化赋值之后便不能再次赋值
基本数据类型:表示的值不能改变
引用数据类型:所引用的地址值不能改变
- final修饰方法时,如果父类拥有final修饰方法,子类继承后不能重写父类中被final修饰的方法
- final修饰类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法
final, finally, finalize的区别
- final 用于声明变量,方法和类,分别表示变量不可变,方法不可覆盖,类不可被继承。
- finally是异常处理语句结构的一部分,表示总是执行。
- finalize()是Object类的一个方法,在GC垃圾回收器执行的时候会调用被回收对象的此方法,一般覆盖此方法用于释放非Java 资源,例如关闭文件等,但因为JVM不保证此方法总被调用,不推荐使用finalize来关闭资源,通常的做法是调用资源相关类提供显示的close()方法去释放资源
你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode 方法?
是为了提高效率,采取重写hashcode方法,先进行hashcode比较,如果不同,那么就没必要在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的
描述深拷贝和浅拷贝
拷贝分 引用拷贝 , 对象拷贝
引用拷贝 —— 即创建一个指向对象的引用变量的拷贝。相当于给一个新变量赋值引用地址操作。
对象拷贝 —— 克隆clone()方法
创建一个新的对象,把原来对象的信息拷贝到新对象, 它们的地址是不同的 。拷贝的对象信息分基本数据类型,引用类型。
对象拷贝分 浅拷贝 , 深拷贝 ——克隆clone()方法
- 浅拷贝
对于数据类型是基本数据类型的成员变量,直接进行值传递,修改不会有影响
对于数据类型是引用数据类型的成员变量,那么会进行引用传递,也就是将该成员变量的引用值(内存地址)复制一份给新的对象,修改会有连锁影响
- 深拷贝
深拷贝是拷贝整个对象信息,除了基本类型的成员变量,还包括引用类型的成员变量。简单来说就是两个独立的个体互不相干。深拷贝相比于浅拷贝速度较慢并且花销较大。
既然有了字节流,为什么还要有字符流?
当我们使用字节流去 读写 中文文本时,如果我们不知道编码类型就很容易出现乱码问题,所以 I/O流 就干脆提供了一个直接操作字符的字符流接口,方便我们平时对字符进行读写操作。
字符流底层还是通过字节流实现的,但是字符流自带缓冲区,当字符流执行读写操作时使用了缓冲区,再通过缓冲区操作文件,这样就减少了I/O流对磁盘的操作 ,从而优化性能
如果读写的是音频文件、图片等媒体文件还是要用回字节流比较好
Java 序列化中如果有些字段不想进行序列化,怎么办?
对于不想进行序列化的变量,使用 transient 关键字修饰
transient 关键字的作用是:阻止对象成员变量被序列化
当对象被反序列化时,被 transient 修饰的变量值不会恢复
transient 只能修饰变量,不能修饰类和方法
获取用键盘输入常用的两种方法
方法 1:通过 Scanner
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();
方法 2:通过 BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
input.close();