JAVA面试宝典2021版
(Java Runtime Environment)是Java 运行时环境……操作编译后Java包括程序所需的包,包括Java虚拟机(JVM)、Java基础类库、Java 命令和其他基础设施。但是,它不能用于创建新程序。
是Java 开发工具包……功能齐全的SDKforJava。它拥有JRE一切都包括编译java编译源代码javac,还有很多java程序调试分析工具:jconsole,jvisualvm等工具软件,也包括java编写程序所需的文件和demo例子程序。它可以创建和编译程序,为程序员提供。
1、功能不同
"=="判断两个变量或实例是否指向同一内存空间的值
"equals"判断两个变量或实例所指向的内存空间值是否相同。
2、定义不同
"equals"在JAVA一种方法。
"=="在JAVA只是运算符合
- Java有基本类型的变量byte、short、char、int、long、float、double、boolean,也被称为原始数据类型。它们之间的比较应用是双等号(==)当使用它们时,比较它们的值==判断两个变量是否相等,如果这两个变量是基本数据类型,只要两个变量值相等,就会放回true
- 复合数据类型(类)
当使用==比较他们的两个值,比较他们的变量指向的地址值,除非这两个变量是相同的NEW出来的对象,他们
比较为true,反之为false
Equals
Java中所有的类都继承与基类Object,在Object中定义了equals方法,这个方法的初始行为是比较对象的内存地址。但是在一些类库中这个方法被重写了,如String、Integer、Date这些类中的equals方法有自身的实现,而不再是比较内存
地址值。
所有说对用复合型数据类型(引用类型),我们推荐使用重写了的equals方法进行比较,
两个对象equals相等,则它们的hashcode必须相等,反之则不一定。两个对象==相等,则其hashcode一定相等, 反之不一定成立。
hashCode(哈希码值)的存在主要应用于查找的快捷性,如hashTable、hashMap等,hashCode码是确定对象在散列存储结构中的地址值。
如果两个对象通过equals方法比较相等,那么他们的hashCode值也一定相等。反之hashCode码相等,但是连个对象的equals比较不一定相等,也就是说这两个对象之间不一定适用于equals方法,只能说明这两个对象都存在于散列存储结构中。
(1)Final修饰类:
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
(2)Final修饰方法:当final修饰的方法后该方法不能被子类重写(可以重载多个final修饰的方法), 注意:重写的前提是子类可以从父类中继承此方法,如果父类中用final修饰的方法同时访问修饰符是private时,子类中则可以定义相同的方法名和参数
(3)Final修饰变量: final在修饰基本数据类型变量时,该变量一经赋值则不许改变,如果修饰的引用数据类型时,则代表着引用的地址值不能改变,但地址值所指向的内容可以改变。
①修饰成员变量时:修饰成员变量时必须显示初始化,初始化方式有两种,第一就是变量声名时直接初始化,二就是变量声明后不初始化,但必须要在这个变量所在的类的所有构造函数中对这个变量进行赋值。
②修饰局部变量时:当函数的参数通过final进行定义时,代表着该参数为只读,可以读取使用该参数,但不能修改该参数值。
Math的round方法是四舍五入,如果参数是负数,则往大的数如,Math.round(-1.5)=-1
String类并不是基本数据类,而是一个类(class),是C++、java等编程语言中的字符串,
Java中八大基本类型数据:
1、字符类型: char 2、基本整型:byte,short,int,long 3、浮点型:float,double 4 布尔类型:boolean”
String : final修饰,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。 StringBuffer : 对字符串的操作的方法都加了synchronized,保证线程安全。
StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用 StringBuilder对象的append、replace、delete等方法修改字符串。
- String是不可变字符串 StringBuffer、StringBuilder是可变字符串。三者的底层都是以char[]形式保存字符串
String底层初始化时默认char[]数组长度为0,StringBuffer、StringBuilder初始化默认的char[]数组长度为16
(因为String不可变,每次对String进行操作都会生成新的字符串)
- StringBuffer中大部分的方法都是被synchronized关键字这就代表着StringBuffer是线程安全的。StringBuilder
中的方法则没有,代表着他是一个线程不安全的。所以在单线程的情况下选择StringBuilder要更快,然而在多线程当中
考虑使用StringBuffer更加安全些
- 在生命变量的时候如果根据情况这个变量声明之后基本上不做修改我自己考虑直接使用String,不同new String()的
方式进行声明那样就不会再堆内存中创建对象,此时String变量直接指向常量池,并且可以复用,效率更高
String str = “i”代表着声明了一个变量str,此时在常量池中创建了一个内容为i的字符窜对象。
String str = new String(“I”);此时代表着创建了两个对象,str引用地址指向堆内存,如果常量池中没有字符窜i,则会
在常量池中创建第二个对象字符窜i。
1. 利用 StringBuffer 或 StringBuilder 的 reverse 成员方法:
public static String reverse(String str) {
return new StringBuilder(str).reverse().toString();
}
2. 利用 String 的 toCharArray 方法先将字符串转化为 char 类型数组,然后将各个字符进行重新拼接:
//自定义方法
public static String reverse(String str){ char[] chars = str.toCharArray(); //创建StringBuilder对象进行拼接 StringBuilder builder = new StringBuilder(); for (int i = chars.length - 1; i >= 0; i--) { builder.append(chars[i]); } return builder.toString();}
3. 利用 String 的 CharAt 方法取出字符串中的各个字符:
public static String reverse1(String str){ //创建StringBuilder对象进行拼接 StringBuilder builder = new StringBuilder(); //获取字符窜长度 int length = str.length(); for (int i = length-1; i >=0; i--) { builder.append(str.charAt(i)); } return builder.toString();}
4.使用递归的方式进行反转
public static String reverse2(String str){ //获取字符窜长度 int length = str.length(); if(length<=1){ return str; } String left = str.substring(0, length/2); String right = str.substring(length/2,length); return reverse2(right)+reverse2(left);}
indexOf(String str):查找指定的字符在当前字符窜第一次出现的索引值
charAt(int index) 返回指定索引处得字符
replace(char oldChar,char newChar): 它是通过用 newChar 替换此字符串中出现的所有 oldChar
trim() 去除字符串两端的空白
split() 分割字符串 返回分割后的字符串数组
getBytes() 返回字符串的byte类型数组
length() 返回字符串的长度
toLowerCase() 字符串转小写
toUpperCase() 字符串转大写
substring() 截取字符串
equals() 字符串比较
抽象类可以没有抽象方法,但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,它也不能再实例化,即不能直接构造一个该类的对象。 如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。
抽象类不能被实例化
抽象类可以有抽象方法,抽象方法只需申明,无需实现
含有抽象方法的类必须申明为抽象类
抽象类的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
抽象方法不能被声明为静态
抽象方法不能用 private 修饰
抽象方法不能用 final 修饰
不能,抽象类是被用于继承的,final修饰代表不可修改、不可继承的。
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStream,OutputStream,Reader,Writer。Java中其他多种多样变化的流均是由它们派生出来的。
BIO是一个连接一个线程。 NIO是一个请求一个线程。 AIO是一个有效请求一个线程。
BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
Files.exists() 检测文件路径是否存在 Files.createFile()创建文件 Files.createDirectory()创建文件夹 Files.delete() 删除文件或者目录 Files.copy() 复制文件 Files.move() 移动文件 Files.size()查看文件个数 Files.read() 读取文件 Files.write()写入文件
List,Map,Set ,Collection ,List ,LinkedList ,ArrayList ,Vector ,Stack ,Set Map ,Hashtable ,HashMap ,WeakHashMap
数据容器主要分为了两类: Collection: 存放独立元素的序列。 Map:存放key-value型的元素对。(这对于需要利用key查找value的程序十分的重要!) 从类体系图中可以看出,Collection定义了Collection类型数据的最基本、最共性的功能接口,而List对该接口进行了拓展。 LinkedList :其数据结构采用的是链表,此种结构的优势是删除和添加的效率很高,但随机访问元素时效率较ArrayList类低。 ArrayList:其数据结构采用的是线性表,此种结构的优势是访问和查询十分方便,但添加和删除的时候效率很低。 HashSet: Set类不允许其中存在重复的元素(集),无法添加一个重复的元素(Set中已经存在)。HashSet利用Hash函数进行了查询效率上的优化,其contain()方法经常被
使用,以用于判断相关元素是否已经被添加过。 HashMap: 提供了key-value的键值对数据存储机制,可以十分方便的通过键值查找相应的元素,而且通过Hash散列机制,查找十分的方便。
Collection 是集合的接口,其继承类又List Set
Collections 是集合的工具类,定义了许多操作集合的静态方法。是帮助类
List:有序集合
Set:不重复集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序
Map:键值对集合
(1)元素的重复性:
List集合中可以出现重复元素
Set集合中不可以出现重复元素,在向Set集合中存储多个重复的元素时会出现覆盖
Map集合采用key-value的形式存储,在Map中不能出现重复的Key键,但可以出现多个不同的Key对应相同的Value
(2)元素的有序性:
List集合及其所有的实现类都确保了元素的有序性
Set集合中的元素是无序的,但是某些Set集合通过特定的形式对其中的元素进行排序,如LinkedHashSet
Map和Set一样对元素进行了无序存储,如:TreeMap根据Key键实现了元素的升序排序
(3)元素是否为空值:
List集合中允许存在多个空值
Set最多允许一个空值出现
Map中只允许出现一个空键,但允许出现多个空值
1 HashMap不是线程安全的 hastmap是一个接口 是map接口的子接口,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值。HashMap允许null key和null value 2 HashTable是线程安全的一个Collection。 HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。 HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。 HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。 Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访
问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。 Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。
TreeMap<K,V> 的Key值是要求实现 java.lang.Comparable ,所以迭代的时候TreeMap默认是按照Key值升序排序的;
TreeMap的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。
HashMap<K,V> 的Key值实现散列 hashCode() ,分布是散列的、均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。适用于在Map中插入、删除和定位元素。
HashMap使用数组加链表实现。每个数组中储存着链表。当使用put方法储存key-value键值对时,会先调用key的hashCode方法,得到此key经特定哈希运算后的值,然后将此值通过其他运算(?)得到一个值,将这个值与(length-1)做或操作(&),相当于对数组长度做取余操作。最终得到一个值作为此key在数组中的索引值,然后将key-value键值对储存进去。通过这种方法将储存的不同key-value键值对“散列”到数组的不同位置。
在储存的时候,如果索引位置尚无元素,那么直接储存。如果有元素,那么就调用此key的equals方法与原有的元素的Key进行比较。如果返回true,说明在这个equals定义的规则上,这两个Key相同,那么将原有的key保留,用新的value代替原来的value。如果返回false,那么就说明这两个key在equals定义的规则下是不同元素,那么就与此链表的下一个结点进行比较,知道最后一个结点都没有相同元素,再下一个是null的时候,就用头插法将此key-value添加到链表上。
HashMap对重复元素的处理方法是:key不变,value覆盖。
当使用get方法获取key对应的value时,会和储存key-value时用同样的方法,得到key在数组中的索引值,如果此索引值上没有元素,就返回null。如果此索引值上有元素,那么就拿此key的equals方法与此位置元素上的key进行比较,如果返回true。就返回此位置元素对应的value。如果返回false,就一直按链表往下比较,如果都是返回false,那么就返回null。
另外:HashMap在JDK1.8之后引入红黑树结构。HashMap是线程不安全的,线程安全的是CurrentHashMap,不过此集合在多线程下效率低。
首先,我们需要知道它是Set的一个实现,所以保证了
• 数据结构实现: ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 • 随机访问效
率: ArrayList 比 LinkedList 在随机访问的时候效率要高, 因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。 • 增加和删除效率: 在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高, 因为 标签: omega变送器os36sswt131变送器