目录
3.Java中表示价格为啥不用double,而是用BigDecimal
7.int和integer的区别,和Integer缓存的实现
10.String str="abc"创建了几个对象?String str=new String("abc")创建了几个对象
11.String,StringBuffer和StringBuilder的区别
22.List三个子类的特点ArrayList 、LinkedList 、voctor
25.HashMap和HashSet添加重复值是替换还是覆盖
27.ArrayList、HashMap和LinkedList的默认空间是多少?扩容机制是什么 ?
1.Java跨平台的原理
Java的运行:
1、编译Java代码生成字节码(.class)文件;不同平台编译的字节码文件是相同的;
2、字节码不能直接运行,必须通过对应的jvm翻译为机器码才能运行
我们编写的Java代码,编译后会生成 .class 文件(字节码文件),(某系统)Java虚拟机负责将字节码文件翻译(转译)成对应系统下的机器码然后运行。也就是说,只要在不同平台上安装对应的JVM,就可以运行Java程序。
在这个过程中,Java程序没有任何改变,仅仅是通过JVM,就能在不同平台上运行,真正实现了“一次编译,到处运行”。
JVM是实现Java程序跨平台的关键。
注意:跨平台的是Java程序,而不是jvm,不同系统需要安装对应版本的JVM;
2.基本数据类型所占字节数
3.Java中表示价格为啥不用double,而是用BigDecimal
float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。
我们的计算机是二进制的。浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。
其实java的float只能用来进行科学计算或工程计算,在大多数的商业计算中,一般采用java.math.BigDecimal类来进行精确计算。
4.双精度&单精度的区别
内存中所占 字节数 | 有效数字位数 | 所表示的数的范围 | 在程序中的处理速度 | |
单精度 | 4字节 | 7 | -3.40E+38 ~ +3.40E+38 | 快 |
双精度 | 8字节 | 16 | -1.79E+308 ~ +1.79E+308 | 慢 |
5.面向对象的特征
5.1.封装:
将对象封装成一个高度自治和相对封闭的个体,对象状态(属性)由这个对象自己的行为(方法)来读取和改变。
封装的优点:
- 将变化隔离
- 便于使用
- 提高重用性
- 提高安全性
但是,变量或者方法由于使用privat修饰,会导致其不能被直接访问,访问、调用的难度和步骤都有增加。
封装的实现形式:
- 使用访问权限修饰符private在定义JavaBean时对于成员变量使用private进行修饰,同时对外提供set、get方法,使用了private修饰的成员在其他类中不能直接访问,此时需要使用set、get方法进行。
- 定义一个Java类与Java的方法就是最简单最常见的面向对象的封装操作,这些操作符合隐藏实现细节,提供访问方式的思路。
5.2.继承
- 面向对象编程(OOP)语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 通过继承创建的新类称为“子类”或“派生类”。
- 被继承的类称为 “基类”、“父类”或“超类”。
- 继承的过程,就是从一般到特殊的过程。
- 要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
- 在Java语言钟,一个类只能单继承,可以实现多个接口。继承就是子类继承父类的特征和行为,使得子类对象具有父类的非private属性和方法。
为什么要实现继承?
- 减少代码重复、臃肿,提高代码可维护性。
继承的特性:
- 子类拥有父类非private得属性和方法
- 子类可以拥有完全属于自己得属性和方法(对父类扩展)
- Java是单继承(每个子类只能继承一个父类);但是Java可以是多重继承(如:A继承B,B继承C)
5.3.多态
多态就是同一个接口,使用不同得实现,而执行不同得操作。
多态得三个必要条件:
- 继承(extends)
- 重写(子类重写父类的同名方法)
- 父类引用指向子类的对象 Parent p=new Son();
子类继承 父类,重写父类的方法,当子类对象调用重写的方法时,调用的是子类的方法,而不是父类的方法,当想要调用父类中被重写的方法时,则需使用关键字super
5.4 .抽象
6.装箱&拆箱
java设计理念就是一切皆为对象,但是基本数据类型不是对象,为了能对基本数据类型进行对象操作,装箱和拆箱应运而生。
装箱:值类型转换为引用对象,一般是转换为System.Object类型或值类型实现的接口引用类型;
装箱就是把值类型转换为引用类型,具体过程:
- 1.在堆中申请内存,内存大小为值类型的大小,再加上额外固定空间(引用类型的标配:TypeHandle和同步索引块);
- 2.将值类型的字段值(x=1023)拷贝新分配的内存中;
- 3.返回新引用对象的地址(给引用变量object o)
int x = 1023;
object o = x; //装箱
拆箱:引用类型转换为值类型,注意,这里的引用类型只能是被装箱的引用类型对象;
拆箱具体过程:
- 1.检查实例对象(object o)是否有效,如是否为null,其装箱的类型与拆箱的类型(int)是否一致,如检测不合法,抛出异常;
- 2.指针返回,就是获取装箱对象(object o)中值类型字段值的地址;
- 3.字段拷贝,把装箱对象(object o)中值类型字段值拷贝到栈上,意思就是创建一个新的值类型变量来存储拆箱后的值;
int x = 1023;
object o = x; //装箱
int y = (int) o; //拆箱
下表是基本数据类型对应的包装器类型:
int(4字节) | Integer |
byte(1字节) | Byte |
short(2字节) | Short |
long(8字节) | Long |
float(4字节) | Float |
double(8字节) | Double |
char(2字节) | Character |
boolean(未定) | Boolean |
7.int和integer的区别,和Integer缓存的实现
- int 是基本数据类型;Integer 是int 的包装类
- int变量不需要实例化就可以使用;Integer变量必须实例化后才能使用;
- int是直接存储数据值;Integer实际是对象的引用,指向此new的Integer对象;
- int的默认值是0;Integer的默认值是null;
两种类型的值的比较:
- 由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。所以两者的比较为false
- Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
- 非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
- 对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false、
integer的缓存机制:
Integer在进行自动拆装箱(java1.5之后)的时候,编译器会使用Integer.valueOf()来构建Integer实例;在valueOf()构建Integer实例的过程中是通过IntegerCache来实现的,IntegerCache值的范围是-128到127(byte)。
在这个范围内是直接返回数值,(这个数值是放置于java内存的栈区的,不需要生成对象。)
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
而当值大于127的时候就new Integer(i);生成了新的对象,而生成的对象放置在堆区也就是说指向了两个不同的内存地址。
对于第4条的原因: java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100)。而java API中对Integer类型的valueOf的定义如下,对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了。
public static Integer valueOf(int i){
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high){
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
8.运算符&&||和&|的区别
|| 和 && 定义为逻辑运算符
| 和 & 定义为位运算符,逻辑运算符
&& 如果两个操作数都true,则条件为真;
|| 如果两个操作数中有任意一个true,则条件为真。
& 按位与操作,按二进制位进行"与"运算。运算规则:(有 0 则为 0)
0&0=0;
0&1=0;
1&0=0;
1&1=1;
| 按位或运算符,按二进制位进行"或"运算。运算规则:(有 1 则为 1)
0|0=0;
0|1=1;
1|0=1;
1|1=1;
9.==和equals的区别
==强调栈中的比较,可以理解为地址比较
equals强调对象的内容比较
10.String str="abc"创建了几个对象?String str=new String("abc")创建了几个对象
首先,我们需要知道JVM的堆中存放对象的实例,而JVM的栈中存储对象的引用。
其次,字符串的分配和其它对象一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多,JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分确定常量池中一定不存在两个相同的字符串。
a、String str = “abc”;创建过程:
1、首先在常量池中查找是否存在内容为"abc"字符串对象。
2、如果不存在则在常量池中创建"abc",并让str引用该对象。(也就是创建1个对象的情况)
3、如果存在则直接让str引用该对象(该对象是存在方法区的运行时常量池中的)。(也就是创建0个对象的情况)
b、String str = new String(“abc”);创建过程:
1、首先在堆中(不是常量池)创建一个指定的对象"abc",并让str引用指向该对象。(创建第1个对象)
2、在字符串常量池中查看是否存在内容为"abc"的字符串对象。
3、若存在,则让堆中创建好的字符串对象对字符串常量池的对象进行引用。(也就是只创建1个对象的情况)
4、若不存在,则在常量池中添加一个内容为"abc"的字符串对象,并将堆中的String对象进行引用。(也就是创建2个对象的情况)
11.String,StringBuffer和StringBuilder的区别
String
String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。注意,在重新给字符串赋值时,并不是对原来堆中实例对象进行重新赋值,而是生成一个新的实例对象,新的实例对象指向想要被赋值的字符串,而引用则指向最新生成的实例对象,之前的实例对象仍然存在,如果没有被再次引用,则会被垃圾回收。
- 字符串不是基本数据类型
- String不是关键字
- String在java.lang包下
StringBuilder
线程不安全 但是快 StringBuffer对象则代表一个字符序列可变的字符串,
数组扩容
不是真正的扩容
是将原数组的内容放到更大的数组中去
StringBuffer
线程安全 但是慢 原因:在它的方法都使用了synchronized修饰,保证了同时只能有一个线程对其进行操作。
使用StringBuffer和StringBuilder的原因
在字符串拼接过程中会产生中间量,大数量的拼接会导致程序的运行变慢。
StringBuffer中使用一个数组来存贮拼接的内容拼接完成后将该数组转为一个字符串,
这样就避免了中间量的产生。执行append方法,向value数组追加字符,
在追加的过程中,如果value数组不能存储要追加的部分,则vlaue数组将进行扩容至 (value.length<<1)+2,
如果还不够,需要多大就给多大
12.String 类可以被继承吗?为什么?
为什么String类要设计为不可被继承的?
String类不可以被继承,因为String类被fianl所修饰,String类实际是一个被final关键字修饰的char[]数组,所以实现细节上也是不允许改变。
不能被继承的原因:
- 效率性,String类作为常用的类,禁止被继承和重写,能够提高效率
- 安全性,String类中有很多调用底层的本地方法,调用了操作系统的 API,如果方法可以重写,可能被植入恶意代码,破坏程序
被final所修饰的类,方法,变量,都不可以被继承,或者修改
值得注意的是,在final关键字修饰引用变量时,引用变量初始化后所指向的对象是不可变的,但是所指向的对象的内容是可以变的。
13.实现一个拷贝文件夹的方法,使用字节流还是字符流
字符类型一般包括:word、txt、文本类型。
字节类型一般包括:图片、声音、图像等)。
因为一般字符流最终都要转换成字节流,所以为考虑到通用性,要用字节流。
14.java中什么是序列化和反序列化?
14.1.序列化和反序列化概念
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
14.2.需要序列化的情况
- 把的内存中的对象状态保存到一个文件中或者数据库中时
- 用套接字在网络上传送对象时
- 通过RMI传输对象时
14.3.如何实现序列化
继承Serializable接口
14.4.设置serialVersionUID
如果我们不进行设置,java会自动给我们生成一个serialVersionUID ,但是如果是这样的话,就很容易存在一种情况,serialVersionUID 在后来扩展的时候由于种种问题与之前的serialVersionUID 不一致了,就会出现无法识别旧数据的问题。所以,手动给定一个serialVersionUID 是很有必要的。例如:简单赋值一个1L。要注意,虽然计算机不区分大小写,但是人会容易看错。
14.5java中不想序列化的字段怎么处理?
对于不想进行序列化的变量,使用 transient 关键字修饰。
transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。
15.深拷贝&浅拷贝
假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝
如果B没变,那就是深拷贝
之所以会有深拷贝与浅拷贝之分,是因为不同数据类型的数据在内存中的存储区域不一样。
深拷贝
package com.sxl.easy;
public class Easy {
public static void main(String[] args) throws CloneNotSupportedException {
Student stu1=new Student();
stu1.name="zhangsan";
Teacher t1=new Teacher();
t1.name="老崔";
stu1.setTeacher(t1);
Student stu2=(Student) stu1.clone();
stu2.name="lisi";
stu2.teacher.name="老黑";
System.out.println(stu1);
System.out.println(stu2);
}
}
class Teacher implements Cloneable{
String name;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}
class Student implements Cloneable{
String name;
Teacher teacher;
public Teacher getTeacher(){
return teacher;
}
public void setTeacher(Teacher teacher){
this.teacher=teacher;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student temp=(Student) super.clone();
Teacher teacher_temp= (Teacher) temp.teacher.clone();
temp.setTeacher(teacher_temp);
return temp;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", teacher=" + teacher +
'}';
}
}
16.什么是Java反射?
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
由实例对象获得类的属性和方法。
17.获取Class对象的方式
- 类名.class
- 对象.getClass()
- Class.forName(类全名)
18.反射常用API类
18.1.获取属性
- getFiled: 获取public类型的属性
- getFileds:获取所有public类型的属性
- getDeclaredField:获取被定义过的属性(包括private、protected)
- getDeclaredFields:获取所有被定义过的的属性
18.2.获取方法
- getMethod:获取public类型的方法
- getMethods:获取所有public类型的方法
- getDeclaredMethod:获取被定义过的方法(包括private、protected)
- getDeclaredMethods:获取所有被定义过的的方法
- invoke(class, method):就是调用Method类代表的方法。可以实现动态调用
18.3.获取构造方法
- getConstructor:获取public类型的构造方法
- getConstructors:获取所有public类型的构造方法
- getDeclaredConstructor:获取被定义过的构造方法(包括private、protected)
- getDeclaredConstructors:获取所有被定义过的构造方法
18.4.创建对象
- newInstance:字节码类型的实例clazz调用newInstance()方法会返回一个Object类型的对象
- Constructors.newInstance:
18.5.获取父类
接口:
- getAnnotatedInterfaces--- AnnotatedType[]:
- getInterfaces---Class[]:
父类:
- getSuperclass:
- getInterfaces:
19.反射的使用步骤
-
获取反射中的Class对象--------三种方式
-
通过反射创建类对象--------通过Class对象或者Constructor对象调用newInstance()方法
-
通过反射获取类属性、方法、构造器-----通过Class对象调用getFields()方法,getDeclareFields()方法或其他
-
反射源码解析
20.Java创建对象的方式
- 用new语句创建对象
- 运用反射手段,调用Java.lang. Class或者java.lang.reflect.Constructor类的newInstance()实例方法
- 调用对象的clone()方法
- 运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法
21.讲一下Java中的集合
Java中集合分为两大类
value
key-value
存储值的又分两种:
- List :有序可重复
- Set :无序不可重复 (如果一个对象存储在Set中,必须重写equals和hashCode方法)
存储key-value的为map
22.List三个子类的特点ArrayList 、LinkedList 、voctor
底层逻辑 | 线程安全 | 查询速度 | 增删速度 | |
ArrayList | 数组 | 不安全 | 快 | 慢 |
voctor | 数组 | 安全 | 快 | 慢 |
LinkedList | 链表 | 不安全 | 慢 | 快 |
23.HashSet如何检查重复
当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会和其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现,但是如果发现有相同的hashcode值的对象,这时候会调用equals方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。
24.HashMap 和 HashSet区别
HashMap | HashSet | |
继承 | 实现Map接口 | 实现Set接口 |
存储 | 存储键值对 | 仅存储对象 |
添加方式 | 调用put方法 | 调用add方法 |
判断相等 | 直接用键值计算hashcode | 使用成员对象计算hashcode值,若有两个对象hashcode值相同,则调用equals方法来判断是否相等 |
25.HashMap和HashSet添加重复值是替换还是覆盖
HashMap的put方法,如果key相同,值不同,则覆盖;
HashSet底层是HashMap,他的值就是HashMap的key,而value则是一个固定的Object对象。在这,相当于key相同,value也相同,故不会替换,因为这个元素已经存在。
26.HashMap容量
在HashMap中,有两个比较容易混淆的关键字段:size和capacity ,这其中capacity就是Map的容量,而size我们称之为Map中的元素个数。
简单打个比方你就更容易理解了:HashMap就是一个“桶”,那么容量(capacity)就是这个桶当前最多可以装多少元素,而元素个数(size)表示这个桶已经装了多少元素。
27.ArrayList、HashMap和LinkedList的默认空间是多少?扩容机制是什么 ?
1、ArrayList 的默认大小是10个元素。扩容点规则是,新增的时候发现容量不够用了,就去扩容。扩容大小规则是:1.5倍
2、HashMap 的默认大小是16个元素(必须是2的幂)。扩容因子默认0.75,扩容机制.(例如:初始大小为 16 ,扩容因子 0.75 ,当容量为12(16*0.75=12)的时候(这个值是size的值也就是put的key-value的数量),触发扩容,扩容后的大小为 32.)
3、LinkedList 是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。