java基础面试
java基础
① final 、finaly、finalize的不同
final 可以用来修饰类、方法、变量,分别有不同的意义,final 修饰的 class 代表不可以继承扩展,final 的变量是不可以修改的,而 final 的方法也是不可以重写的(override)。
finally 则是 Java 保证重点代码一定要被执行的一种机制。我们可以使用 try-finally 或者 try-catch-finally 来进行类似关闭 JDBC 连接、保证 unlock 锁等动作。
finalize 是基础类 java.lang.Object 的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9 开始被标记为 deprecated(弃用)。
② java数组排序查找
重点冒泡 选择 二分法查找
1) 冒泡
int[] arr = {23,12,48,56,45}; int temp = -1; for(int i=0;i<arr.length;i++) { for(int j=i+1;j<arr.length;j++) { if(arr[i]>arr[j]) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } System.out.println(Arrays.toString(arr)); |
2) 选择
int[] arr = {23,12,48,56,45}; for(int i=0;i<arr.length;i++) { int tem = i; for(int j=i;j<arr.length;j++) { if(arr[j] < arr[tem]) { tem = j; } } int temp1 = arr[i]; arr[i] = arr[tem]; arr[tem] = temp1; } System.out.println(Arrays.toString(arr)); |
3) 二分
int arr [] = new int [5]; int min = 0; int max = arr.length - 1; int mid = (min + max)/2; //tager是要查找的数 while(true){ if(tager > mid){ min = mid + 1; }else if(tager < mid){ max = mid -1; }else{ return mid; } if(min > max){ return -1; }
mid = (max + min )/2; } |
③ java String 的用法 和内存机制 、其中方法(重点equals(),substring())
我写一下String的内存机制,其他内容较为简单,请大家查阅学习。
字符串内存机制:
JVM运行的时候,将内存分为两个部分,一部分是堆,一部分是栈。堆中存放的是创建对象,而栈中存放的则是方法调用过程中的局部变量或引用。在设计JAVA字符串对象内存实现的时候,在堆中又开辟了一块很小的内存,称之为字符串常量池,专门用来存放特定的字符串对象的。
String s1="JavaSE6.0"; //存放在字符串常量池中。性能高 String str = new String("JavaSE6.0"); //存放在堆中
s1==str ; //返回false s1.squals(str); //返回 true 补充:==和String equals()的比较 String equals()重写了Object中的equals()方法,Object的equals()方法就是==比较,String equals()方法变成内容比较,==比较的是地址hashcode值,简单的说成是比较对象的地址,如果对象引用的地址相同,那么相当于对象是同一个对象。
|
题型:
String str=new String(“good”) ;//创建了几个对象 String str1=”good”;//创建了几个对象 String str2=new String (“good”);//创建了几个对象
System.out.println(str==str1);//输出什么 System.out.println(str.equals(str2));//输出什么 System,out,println(str1==str2);//输出什么
① String常量池中查找有没有字符常量“good”。因为没有所以创建“good”对象,当执行new String(“good")时,则在Java的堆中创建一个”good”对象,而str是该对象的引用,因此共计创建2个对象。 ② 首先在String常量池中查找有没有字符串常量“good",有则直接将str1作为String常量池中“good”的一个引用,当你重新声明一个String型变量为“good”时,将使用串池里原来的那个“good",而不用重新分配内存,也就是说,str与str1将会指向同一块内存,因此,此时没有创建任何对象。 ③ String常量池中查找有没有字符串常量“good”,有则不进行再次创建,由于这里用了new关键字(有new就有对象),所以在Java堆中又创建了一个“good”对象(地址与第一句在堆中创建的地址不同),而str2则是这个对象的引用,因此执行此句时,创建了1个对象。 ④ “==”是判断对象的,由于str指向的是Java堆中的“good”对象,而str1指向的是String常量池中的“good”对象,所以返回值为false。 ⑤ 由于String类中的equals判断的是对象的内容而不是内存地址,由于所有内容都是good,所以返回值为true。 ⑥ “==”是判断对象的,由于str1指向的是Java堆中的“good”对象,而str2指向的是堆中的“good”对象,但是使用了new关键字,说明这两个对象不是同一个对象,所以返回值为false |
④ 集合分类
connection:
1.list:List里存放的对象是有序的,同时也是可以重复的。子接口:
1)linkedlist:底层是一个链表结构,增删速度快。线程不是安全的。
2)arraylist:底层是一个数组结构,查询速度快,线程是不安全的。内有sort
方 法可为arraylist元素自动排序。
3)vector:底层数组结构,在vector的操作方法中加了锁,所以线程安全。
2.set: Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排
序,只是简单地把对象加入集合中
1)hashset:底层是一个hashmap结构,非线程安全。
2)treeset:基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对
元素进行排序,或者根据创建 set 时提供的 Comparator进行排序,具
体取决于使用的构造方法。
map:Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。子接口:
hashmap: 数组方式存储key/value,线程非安全,允许null作为key和value,
key不可以重复,value允许重复,不保证元素迭代顺序是按照插入时的顺序,
key的hash值是先计算key的hashcode值,然后再进行计算,每次容量扩容会
重新计算所以key的hash值,会消耗资源,要求key必须重写equals和hashcode
方法
默认初始容量16,加载因子0.75,扩容为旧容量乘2,查找元素快,如果key
一样则比较value,如果value不一样,则按照链表结构存储value,就是一个
key后面有多个value;
这里介绍一下hashmap的子类并且也是map接口的子类。
linkedhashmap: LinkedHashMap可以认为是HashMap+LinkedList,即它既使用
HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序。线程安全这一点是我们选用linkedhashmap而不选用hashmap的主要原因。
大家看一下源码,内有多种构造方法,这里不再一一赘述。
集合中有很多方法也是经常问到的,大家自己去分类把握。
再说一下集合的遍历:
① .对于Arraylist 使用迭代器进行遍历
ArrayList<String> list=new ArrayList<>(); list.add("jeremy"); list.add("张三"); list.add("李四");
Iterator iterator=list.iterator();//调用Arraylist中的迭代器方法 //返回的是Iterator 类型
while(iterator.hasNext()) { // hasNext() 方法 判断迭代器中是否有可以迭代的元素 //并将指针下移 Object item=iterator.next(); System.out.println(item);
} |
② .对于set的遍历
//和list 集合中的遍历方法一致 区别在于set集合没有顺序 // 不可以根据下标获得 Set<String > set=new HashSet<>(); set.add("jeremy"); set.add("张三"); set.add("李四");
//使用iterator 遍历 Iterator< String > iterator=set.iterator(); while(iterator.hasNext()) {
Object object=iterator.next(); System.out.println(object);
}
//可以使用foreach循环遍历 for(String item:set) { System.out.println(item); } } |
② 对于map的遍历
map的存储格式为键1----值 1,键2-----值2. 可根据get(key)方法 来取得值,key 为键
并且map也是一个无序 的集合 ,map没有自带的iterator()方法, 要遍历map,除了使用foreach
我们需要使用一个''中间人''. map中有一个方法为keyset();可以得到键,返回一个Set对象.
//创建一个map Map<String, Object> map=new HashMap<>();
//添加数据 map.put("name", "james"); map.put("name1", "张三"); map.put("name3", "李四");
//使用set作为中间人 接收键名 Set<String > set=map.keySet();
//调用set中的迭代器 Iterator< String > iterator=set.iterator(); while(iterator.hasNext()) { Object object= iterator.next(); //根据健名获得键值 System.out.println(object+"-----"+map.get(object)); } |
5.java的三大特性
1.继承
只能单继承,一个类只能有一个父类,易于管理程序,父类是子类的一般化,子类
是父类的特化(具体化)。
作用:
1) 继承关系是传递的。若类C继承类B,类B继承类A(多继承),则类C既有从
类B那里继承下来的属性与方法,也有从类A那里继承下来的属性与方法,还可以有自己新定义的属性和方法。继承来的属性和方法尽管是隐式的,但仍是类C的属性和方法。
2) 继承提供了软件复用功能。若类B继承类A,那么建立类B时只需要再描述与基
类(类A)不同的少量特征(数据成员和成员方法)即可。这种做法能减小代码和数据的冗余度,大大增加程序的重用性。
2.堕胎:方法的重写、重载与动态连接构成多态性
向上转型:
我定义了一个子类Cat,它继承了Animal类,那么后者就是前者的父类。我可以通过
Cat c = new Cat(); 例化一个Cat的对象,这个不难理解。
但当我这样定义时: Animal a = new Cat();
这代表什么意思呢?
很简单,它表示我定义了一个Animal类型的引用,指向新建的Cat类型的对象。由于Cat是继承自它的父类Animal,所以Animal类型的引用是可以指向Cat类型的对象的。那么这样做有什么意义呢?因为子类是对父类的一个改进和扩充,所以一般子类在功能上较父类更强大,属性较父类更独特,定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。所以,
父类引用只能调用父类中存在的方法和属性,不能调用子类的扩展部分;因为父类引用指向的是堆中子类对象继承的父类;(但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了。)
同时,父类中的一个方法只有在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;
对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将会调用子类中的这个方法,这就是动态连接。
向下转型:
向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
向下转型只能转型为本类对象
3.封装
封装就是把同一类事物的共性(包括属性和方法)归到同一类中,方便使用。也就是说封装:封装也称信息隐藏,是指利用抽象数据类型把数据和基于数据的操作封装起来,使其成为一个不可分割的整体,数据隐藏在抽象数据内部,尽可能的隐藏数据细节,只保留一些接口使其与外界发生联系。也就是说用户无需知道内部的数据和方法的具体实现细节,只需根据留在外部的接口进行操作就行
实现封装:通常将类的成员变量声明为private,在通过public方法来对这个变量来访问。对一个变量的操作,一般有读取和赋值2个操作,,我们分别定义2个方法来实现这2个操作,一个是getXX(XX表示要访问的成员变量的名字)用来读取这个成员变量,另一个是setXX()用来对这个变量赋值
6.访问权限范围控制关键字:
1.pubilc 在项目下可访问
2.default(默认不写) 本类,本包访问
3.protect 本类,本包子类访问
4.private 本类访问
7.重写重载的用法和区别
重写: 在java中有很多的继承,继承下来的有变量、方法。在有一些子类要实现的方法中,方法名、传的参数、返回值跟父类中的方法一样,但具体实现又跟父类的不一样,这时候我们就需要重写父类的方法
重载:重载是在一个类中实现的,有多个同名方法,但参数不一样,包括参数类型、参数个数、还可以没有参数,总之每个重载的方法的参数必须不一样。
区别:
区别点 |
重载 |
重写 |
参数列表 |
必须不同 |
必须相同 |
返回类型 |
可以不同 |
必须相同 |
异常 |
可以不同 |
不能抛出新的异常或更高的异常 |
访问 |
可以不同· |
不能有更严格的访问限制 |
8. this和super关键字
时间关系,咱们下次再说。