Java基础面试题-1
Java基础
java基本语法,java特性
1.为什么要重写equals还要重写hashcode
在面经里面有,hashcode重写才会让equals重写有意义
equals()方法用于判断两个对象是否相等,hashCode()方法用于计算对象的哈希码。
2.map分类和常见的情况
Map主要用于存储健值对,根据键得到值,因此不允许键重复,但允许值重复
1.HashMap 存储数据采用的数组+链表或者红黑树结构,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。不安全
2.Hashtable 它不允许记录的键或者值为空;它支持线程的同步, 安全
3.LinkedHashMap是HashMap子类,存储数据采用的哈希表结构+链表结构
4.TreeMap 红黑树,能把保存的数据根据键排序,默认按键值的升序排序
5.ConcurrentHashMap 代替 hashtable
HashMap最多只允许一条记录的键为NULL,允许多条记录的值为NULL.HashMap不支持线程同步,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致性。如果需要同步,可以用 Collections的 synchronizedMap方法使 HashMap具有同步的能力。
2.2hashMap的底层原理
1.8之前是数组+链表,每添加一个元素key-value,通过key的hashcode得到哈希码,然后hash算法得到元素在数组中的位置,如果位置存在元素,就用equals判断当前元素是否已经重复,重复就覆盖,不重复就直接将该key-value放在链表的后面,同一个链表上的元素hash值相同
1.8之后当链表的长度过长,超过8时,则将链表转化为红黑树,更加便于查询。
当链表数组的已经用到自身长度的0.75倍时,将散列链表数组扩容两倍
3.==和equals
面经里面有
java初始化的加载顺序为
父类静态成员变量 父类静态代码块 子类静态成员变量 子类静态代码块 父类非静态成员变量,父类非静态代码块,父类构造函数,子类非静态成员变量,子类非静态代码块,子类构造函数
4.jkd8新特性
1.Lambda表达式 Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。
2.方法引用 可以直接引用已有类和对象的方法或构造器。
3.Stream API 引入了函数式编程风格
4.Date Time 加强对日期和时间处理
5.Lambda表达式
(传递的参数列表)-> { 方法真正作用的代码 }
6.java基本数据类型
八大基本类型 整数 浮点数 字符 布尔
字节型 byte 1字节 Byte
短整型 short 2字节 Short
整数 int 4字节 Integer
长整数 long 8字节 Long
单精度浮点数 float 4字节 Float
双精度浮点数 double 8字节 Double
字符型 char 2字节 Character
布尔类型 boolean 1字节 Boolean
变量:数据类型 变量名 = 数据值
类型转换:
- 自动转换:小范围类型自动提升大范围类型
- 强制转换:将 取值范围大的类型 强制转换成 取值范围小的类型
double 类型内存8个字节,int 类型内存4个字节。想要赋值成功,只有通过强制类型转换,将 double 类型强制转换成 int 类型才能赋值
7.数组( Array)和列表( Arraylist)有什么区别?什么时候应该使用 Array而不是 Arraylist?
Array可以包含基本数据类型和对象类型,大小固定
Arraylist只能包含对象类型,可以扩容,Arraylist提供了更多的方法和特性
如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里,但是如果我们单纯只是想要以数组的形式保存数据,而不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择ArrayList。
而且还有一个地方是必须知道的,就是如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,那么,使用ArrayList就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么,我们可以考虑选择LinkedList。
8.String、StringBuffer、StringBuilder区别
1.可变性
String类中用了final修饰字符数组来保存字符串,private final char value[],所以String对象是不可变的,jdk-9之后是byte[]
StringBuilder与StringBuffer都继承AbstractStringBuilder类,没有final修饰,所以可以改变
2.线程安全性
String中的对象是不可变的,线程安全。 操作少量数据
StringBuilder没有加,所以线程不安全 单线程操作字符串缓冲区
StringBuffer加了方法同步锁,所以线程安全 多线程操作字符串缓冲区
9.int和Integer区别
Integer是int封装类,char-character
1.Integer变量必须实例化后才能使用,而int变量不需要
2.Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
3.Integer的默认值是null,int的默认值是0
10. &和&&区别
&:按位与和逻辑与,用的少
&&:短路与,左边表达式flase就不会判断右边,用的多
11.java中的正则表达式
正则:正则表达式就是在进行字符串匹配和处理的时候最为强大的工具
java中的 String类提供了支持正则表达式操作的方法,包括: matches()replaceAll()
replacefirst() split()此外,Java中可以用 Pattern类表示正则表达式对象,它提供了丰
富的API进行各种正则表达式操作
java关键字
1.Syncronized
synchronized修饰静态方法以及同步代码块,线程想要执行对应同步代码,需要获得类锁。
synchronized修饰成员方法,线程获取的是当前调用该方法的对象实例的对象锁。
3.java锁:Synchronized和lock
synchronized是关键字
是java的内置语言实现的,当发生异常时,会自动释放线程占有的锁
Lock是一个接口
发生异常时,没有主动unLock方法去释放锁,会造成死锁,需要在finally中unLock。
通过Lock可以知道线程有没有成功获得锁。
lock可以让等待锁的线程中断
4.volatile
保证线程有序性和可见性
创建类的实例对象过程
- 为singleDCL 分配内存空间
- 初始化对象
- 分配内存地址
JVM 具有指令重排的特性,执行顺序可能会改变。单线程不会出现问题,多线程会导致⼀个线程获得还没有初始化的实例。Volatile可以禁止jvm的指令重排
5 final
1.修饰类,这个类不能被子类继承
2.修饰成员变量,数值初始化后将不能改变
3.修饰成员方法,不能子类重写
final 变量:
final 变量能被显式地初始化并且只能初始化一次。被声明为 final 的对象的引用不能指向不同的对象。但是 final 对象里的数据可以被改变。也就是说 final 对象的引用不能改变,但是里面的值可以改变。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
final 方法
类中的 final 方法可以被子类继承,但是不能被子类修改。
声明 final 方法的主要目的是防止该方法的内容被修改。
如下所示,使用 final 修饰符声明方法。
final 类
final 类不能被继承,没有类能够继承 final 类的任何特性。
6.static
static方法就是没有this的方法。
在static方法内部不能调用非静态方法,反过来是可以的。
可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。
静态变量和静态方法存储再方法区中的静态区,可以被类名直接调用。main方法
1.修饰成员变量 -静态变量
该类的所有对象都可以访问类变量,改变类变量的值,也可以不创建对象操作类变量
static 数据类型 变量名;
非static变量需要实列化创建后才能访问
2.修饰成员方法 -静态方法
静态方法可以直接访问类变量和静态方法。
静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。
静态方法中,不能使用this关键字。
修饰符 static 返回值类型 方法名 (参数列表){
// 执行语句
}
3.静态代码块
定义在成员位置,使用static修饰的代码块{ }。
位置:类中方法外。
执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。
作用:给类变量进行初始化赋值。
public class ClassName{
static {
// 执行语句
}
}
java面向对象
1.重写和重载
重写,经常使用 Override
重写发⽣在运⾏期,是⼦类对⽗类的允许访问的⽅法的实现过程进⾏重新编写。
1.返回值类型和方法名,参数列表必须相同,抛出异常范围小于等于父类,访问修饰符范围⼤于
等于⽗类。
2.如果父类方法访问修饰符为 private/final/static ,子类就不能重写该方法
3.构造方法不能被重写
参数列表:个数不同,数据类型不同,顺序不同。
重载 Overload
指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
重载就是同⼀个类中多个同名⽅法根据不同的传参来执⾏不同的逻辑处理。
2.Java ⾯向对象编程三⼤特性: 封装 继承 多态
封装 JavaBean
封装把一个对象的属性私有化,同时提供一些可以被外界访问属性的方法
继承
1.子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。。
2.子类拥有⾃⼰属性和⽅法,即⼦类可以对⽗类进⾏扩展。
3.子类重写父类的方法
多态
不同类的对象根据同一消息做出不同的行为方式
在 Java 中有两种形式可以实现多态:继承(多个⼦类对同⼀⽅法的重写)和接⼝(实现接⼝并覆盖接
中同⼀⽅法)。
- 一类事物的行为,具有多种表现形式。
- 条件:
- 继承或者实现【二选一】
- 方法的重写。
- 父类引用指向子类对象。
3.类和对象区别
类是有共同点的事物的描述,抽象的。对象是类的实例,具体的。
类是一组具有相同属性的对象集合体
4.Object类的方法
object()默认构造方法
1.clone 创建对象
2.equals 比较对象是否相等
2.2 toString返回对象的字符串表示形式
3.hashCode 返回对象的哈希值
4.getClass 返回对象运行时的类
5.notify 唤醒在对象监视器上等待的线程
6.notifyAll 多个线程
7.wait 线程等待
5.泛型:参数化类型
定义方法时有形参,然后调用方法传递实参。将原来具体的类型参数化,没调用方法时我们并不知道传递的参数类型,调用方法时再传入具体的参数类型
6.接口和抽象类区别
接口中的方法都是抽象的,类可以实现多个接口,声明的变量默认是final,成员方法默认public
抽象类可以同时有抽象和非抽象方法,子类只能继承一个抽象类,可以包含非final变量,可以是private,protected
7.Comparator接口?
Comparator接口的compareTo方法对象排序,大于返回正数,等于0,小于负数。
compare方法参数排序,大于正数,等于0,小于负数
8.异常
1.throws Exception{} throws作用在成员方法上
try{
//预防可能有异常的代码
}catch(Exception e){
//捕获异常,throw抛出异常
e.printStackTrace();
}finally{
//无论是否异常都会执行代码
}
9.java面向对象设计的“六原则一法则”
1.单一职责原则 :一个类只做自己该做的事,高内聚低耦合,一个代码模块只完成一个功能
2.开闭原则:封装,扩展开发,修改关闭
3.接口隔离原则:接口小而
4.依赖倒转原则:IOC
5.替换原则:任何时候都可以用子类替代父类
6.合成聚合复用原则:优先使用聚合和合成的代码
7.迪米特法则-最少知识原则:对象调用更少的对象
10.Java创建对象几种方式
显示调用构造函数
- new
- 反射
不调用构造函数
- 对象的clone
- 反序列化
11.为什么 Java 中只有值传递
按值传递表示方法接收的是调用者提供的值
按引用调用表示方法接收的是调用者提供的变量地址。
一个方法可以修改传递引用所对应的变量值,却不能修改传递值所对应的变量值。
所以Java总是采用按值调用,方法得到都是所有参数值的一个副本拷贝,方法不能修改传递给它的任何参数变量的内容
12.构造方法
调用构造方法创建新对象,除了接口和抽象类,每个类都有构造函数。
java编译器会自动创建一个默认无参构造函数
13.序列化Serializable和反序列化
实现Serializable接口可以把对象数据分解成字节流,以便存储在文件持久化或在网络上传输
反序列化就是打开字节流并重构对象。
14迭代器Iterator
遍历Set和List集合,而ListIterator只能遍历List
next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
hasNext() 用于检测集合中是否还有元素。
remove() 将迭代器返回的元素删除。
// 创建集合
ArrayList<String> sites = new ArrayList<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
// 获取迭代器
Iterator<String> it = sites.iterator();
// 输出集合中的所有元素
while(it.hasNext()) {
System.out.println(it.next());
}
JavaSE高级
1.java反射机制-Java高级特性
0.概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
本质:java文件编译生成.class文件,反射将.class字节码文件载入时,JVM将产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的方法和属性。
1.使用场合
在编译时根本无法知道该对象或类可能属于哪些类
程序只依靠运行时信息来发现该对象和类的真实信息
2.主要作用
通过反射可以使程序代码访问装载到JVM 中的类的内部信息,获取已加载类的属性信息,获取已加载类的方法信息
3.反射的优点
反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的
4.缺点
性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
使用反射会模糊程序内部逻辑:程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
java反射常用的三种方式
1.通过new对象实现反射机制 -new 类
2.通过路径实现反射机制 -Class.forName(类路径)
3.通过类名实现反射机制 -类名.Class
public class Student {
private int id;
String name;
protected boolean sex;
public float score;
}
public class Get {
//获取反射机制三种方式
public static void main(String[] args) throws ClassNotFoundException {
//方式一(通过建立对象)
Student stu = new Student();
Class classobj1 = stu.getClass();
System.out.println(classobj1.getName());
//方式二(所在通过路径-相对路径)
Class classobj2 = Class.forName("fanshe.Student");
System.out.println(classobj2.getName());
//方式三(通过类名)
Class classobj3 = Student.class;
System.out.println(classobj3.getName());
}
}
与反射机制的相关类
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
1.Class类
1.getClassLoader() 类加载器 Proxy.newProxyInstance(lenovo.getClass().getClassLoader(),创建动态代理对象的一个参数
2. getName() 获得类的完整路径名字
3. newInstance() 创建类的实例
4. getSimpleName() 获得类的名字 。。。。
1.getFields() 获得改类的所有属性成员变量
2.getConstructors() 获得该类的所有公有构造方法
3.getMethods() 获得该类所有公有的方法
2.Field类
Field代表类的成员变量(成员变量也称为类的属性)。
方法 | 用途 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
2.Method类
invoke 传递object对象及参数调用该对象对应的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
和AOP的联系
可以查看AOP源代码,实现java.lang.reflect.InvocationHandler接口
此接口中只有一个抽象类需要实现,public Object invoke(Object proxy,Method method, Object[] args)throws Throwable
同时还需要一个返回代理对象的方法,具体实现:在返回代理对象的方法中调用java.lang.reflect.Proxy.newProxyInstance()方法,该方法会返还一个Object,这个Object就是代理对象
.java文件,就是当前编写的代码文件
.class文件,就是编译过后的文件(jvm只识别.class文件)
2 .类加载过程
1.加载 在内存中生成一个代表这个类的Class对象
2.连接
-验证 确保Class文件的子节流包含信息符合jvm的要求,并不会危害jvm安全
-准备 分配内存空间
-解析 jvm将常量池中的符号引用替换直接引用
3.初始化
4.使用
5.卸载