从java最基础的基本类型到网络,遍历一遍难点,不管从 java编程思想,还是从其他书籍学习,java难点集中在,集合,接口,抽象类,四中类,线程等几个方面是比较难的部分。
java基本类型:
Java整型
int | 4字节 | -2147483648 ~ 2147483647 (正好超过20亿) |
short | 2字节 | -32768 ~ 32767 |
long | 8字节 | -9223372036854775808 ~ 9223372036854774807 |
byte | 1字节 | -128 ~ 127 |
浮点类型
float | 4字节 | 大约±3.40282347E+38F (有效位数为6-7位) |
double | 8字节 | 大约±1.79769313486231570E+308 (有效位数为15位) |
一些需要注意:
浮点数值不适合用于禁止出现舍入误差的金融计算中。例如System.out.println( 2.0 - 1.1);将打印0.899999999999999,而不是0.9。因为浮点数值采用二进制系统表示,而二进制无法精确表示分数1/10,就像十进制无法精确表示1/3一样。如果需要在数值计算中不含有舍入误差,就应该使用BigDecimal类。
char类型
在Java中,char类型用UTF-16编码描述一个代码单元。强烈建议不要在程序中使用char。java的char类为16位。
boolean类型
在C或C++中数值或指针可以代替boolean的值,0相当于flase,非0相当于true,而在Java中则不行,并且在编译时就会报错。
有关类型转换的问题:
short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
short s1 = 1; s1 = s1 + 1; (s1+1运算结果是int型,需要强制转换类型)
short s1 = 1; s1 += 1;(可以正确编译)
Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math.round(11.5)==12
Math.round(-11.5)==-11
round方法返回与参数最接近的长整数,参数加1/2后求其floor.
返回最接近参数的 long。通过加上 1/2 将该结果舍入为整数,取结果的基数并将其强制转换为 long 类型。换句话说,结果等于以下表达式的值:
(long)Math.floor(a + 0.5d)
String s= new String( "xyz ");是几个对象?
知道在java中除了8中基本类型外,其他的都是类对象以及其引用。所以 "xyz "在java中它是一个String对象.对于string类对象来说他的对象值是不能修改的,也就是具有不变性。
看:
String s= "Hello";
s= "Java ";
String s1= "Hello";
String s2=new String( "Hello");
s所引用的string对象不是被修改了吗?之前所说的不变性,去那里了啊?
你别着急,让我告诉你说发生了什么事情:
在jvm的工作过程中,会创建一片的内存空间专门存入string对象。我们把这片内存空间叫做string池。
String s= "Hello ";当jvm看到 "Hello ",在string池创建string对象存储它,并将他的引用返回给s。
s= "Java ",当jvm看到 "Java ",在string池创建新的string对象存储它,再把新建的string对象的引用返回给s。而原先的 "Hello "仍然在string池内。没有消失,他是不能被修改的。
所以我们仅仅是改变了s的引用,而没有改变他所引用的对象,因为string对象的值是不能被修改的。
String s1= "Hello ";jvm首先在string池内里面看找不找到字符串 "Hello ",找到,返回他的引用给s1,否则,创建新的string对象,放到string池里。这里由于s= "Hello "了,对象已经被引用,所以依据规则s和s1都是引用同一个对象。所以s==s1将返回true。(==,对于非基本类型,是比较两引用是否引用内存中的同一个对象)
String s2=String( "Hello ");jvm首先在string池内里面看找不找到字符串 "Hello ",找到,不做任何事情,否则,创建新的string对象,放到string池里面。由于遇到了new,还会在内存上(不是string池里面)创建string对象存储 "Hello ",并将内存上的(不是string池内的)string对象返回给s2。所以s==s2将返回false,不是引用同一个对象。
好现在我们看题目:
String s= new String( "xyz ");
首先在string池内找,找到?不创建string对象,否则创建,这样就一个string对象
遇到new运算符号了,在内存上创建string对象,并将其返回给s,又一个对象
所以总共是2个对象
先看下Object对象的几个方法。
protected Object | clone() 创建并返回此对象的一个副本。 |
boolean | equals(Object obj) 指示某个其他对象是否与此对象“相等”。 |
protected void | finalize() 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 |
Class<? extends Object> | getClass() 返回一个对象的运行时类。 |
int | hashCode() 返回该对象的哈希码值。 |
void | notify() 唤醒在此对象监视器上等待的单个线程。 |
void | notifyAll() 唤醒在此对象监视器上等待的所有线程。 |
String | toString() 返回该对象的字符串表示。 |
void | wait() 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。 |
void | wait(long timeout) 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。 |
void | wait(long timeout, int nanos) 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。 |
在看看集合大家族。
集合框架中的接口
Collection Map
Set List SortedMap
SortedSet
集合框架中的实现类
实现是继承,虚线是实现
Set------------->HashSet List---->ArrayList Map---------->HashMap
| ------------->LinkedHashSet ---->LinkedList |
SortedSet----->TreeSet SortedMap-->TreeMap
Collection:集合层次中的根接口,jdk没有提供这个接口直接的实现类。
提供了add方法,把元素加入到集合当中,但是没有get方法
Set:不能包含重复的元素。SortedSet是一个按照升序排列元素的Set。
List:是一个有序的集合,可以包含重复的元素。提供了按索引访问的方式。
对Collection增加了一个get(int incex)方法,按照索引过去元素
Map:包含了key-value对。Map不能包含重复的key。SortedMap是一个按照升序排列key的map。
Java当中通用的访问集合的方法是Iterator接口
面试难点
List Map Set 区别是什么?
首先这是Collection , Map和的区别问题。List,Set是实现了Collection接口而 Map是与Collection并行的接口。
1、Collection 和 Map 的区别
容器内每个为之所存储的元素个数不同。
Collection类型者,每个位置只有一个元素。
Map类型者,持有 key-value pair,像个小型数据库。
2、各自旗下的子类关系
Collection
--List:将以特定次序存储元素。所以取出来的顺序可能和放入顺序不同。
--ArrayList / LinkedList / Vector
--Set : 不能含有重复的元素
--HashSet / LinkHashSet,TreeSet
Map
--HashMap
--HashTable
--TreeMap
3、其他特征
* List,Set,Map将持有对象一律视为Object型别。
* Collection、List、Set、Map都是接口,不能实例化。
继承自它们的 ArrayList, Vector, HashMap是具象class,这些才可被实例化。
* vector容器确切知道它所持有的对象隶属什么型别。vector不进行边界检查。
集合实现的遍历问题,java,和jstl俩种?
对List的遍历有三种方式
List<A> list = new ArrayList<A>();
list.add(new A());
list.add(new A());
第一种:
for(Iterator<A> it = list.iterator(); it.hasNext(); ) {
....
}
这种方式在循环执行过程中会进行数据锁定, 性能稍差, 同时,如果你想在寻欢过程中去掉某个元素,只能调用it.remove方法, 不能 使用list.remove方法, 否则一定出并发访问的错误.
第二种:
for(A a : list) {
.....
}
内部调用第一种, 换汤不换药, 这种循环方式还有其他限制, 不建议使用它
第三种:
for(int i=0; i<list.size(); i++) {
A a = list.get(i);
...
}
内部不锁定, 效率最高, 但是当写多线程时要考虑并发操作的问题!
JSTL中遍历List
<%
pageContext.setAttribute("voList", voList);
%>
<c:forEach var="vo" items="${voList}">
<c:out value="${vo}" />
</c:forEach>
用Iterator来遍历Set
//实例化HashSet对象
HashSet hs = new HashSet();
hs.add(new String("第一个元素"));
//没有get方法 只能遍历去元素
/*
* 得到Iterator,然后遍历输出
*/
public void show1(HashSet hs){
Iterator i = hs.iterator();
while(i.hasNext()){
String temp = (String)i.next();
System.out.println(temp);
}
}
/*
* 转换成数组,遍历并输出HashSet中的元素
*/
public void show2(HashSet hs){
Object o[] = hs.toArray();
for(int i=0;i<o.length;i++){
System.out.println((String)o[i]);
}
}
Set的JSTL遍历与List一样
java中使用HashMap是主要有两种遍历方法,代码如下:
HashMap a = new HashMap();
a.put("name", "abcdef"); // key是name,value是字符串abcdef
a.get("name"); //Object类型key来获取值
第一种:
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}
效率高,以后一定要使用此种方式!
第二种:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}
效率低,以后尽量少使用!
JSTL遍历MAP元素
对于keySet其实是遍历了2次,一次是转为iterator,一次就从hashmap中取出key所对于的value。而entryset只是遍历了第一次,他把key和value都放到了entry中,所以就快了。
<%
Map map = new HashMap();
map.put("a", "12345");
map.put("b", "abcde");
out.println(map.get("a"));
request.setAttribute("map",map);
%>
<c:forEach items="${map}" var="mymap" >
<c:out value="${mymap.key}" />
<c:out value="${mymap.value}" />
</c:forEach>
ArrayList 和 LinkList 区别
ArrayList底层用数组来完成。而LinkList是双向链表,每个元素里有除了本身之后还有指向前后元素的地址内容。
而在操作上,ArrayList地址链接,循环快。但是如果增加数据或者删除数据就会整个数组要重新copy一份在赋值,所以慢。
LinkList 内存地址散列,遍历慢。加元素,删除元素,之需要前后元素的地址,所以快。
HashMap和HashSet以及HashTable区别
HashSet是Set接口的实现,元素不可重复。对与自己建立的对象而言,情况如下:
以上代码上看到,String'类的eques方法和tostring是已经实现了hashcode的,所以加入HashSet不会重复,对于Student来说默认的equese和tostring是不可以实现对象的一直的,tostring返回的是对象内存地址。
这2个方法的重写可以保证Set中加入的对象已经是重复的。
要实现Set的排序也要重写hashCode方法
HashMap
HashMap对key进行散列。
keySet()、values()、entrySet()。
实现了Map接口的hash表,此实现提供了所有可选的map操作,允许空值和空键 null values null key
跟Collection接口无关系 没有add
put(Object key,Object value) get(Object key)
Set keySet()返回map当中键的视图
Collection values()返回值的视图
Set entrySet()返回键值对的视图
返回的每个元素都是Map.Entry类型,静态接口Map.Entry 在Map的成员变量。
其中有 Object getKey() 和 Object getValue()方法
循环代码如下:
从这里看出Set不允许重复元素,但值中可以有null 切只有一个
Map的Key是可以为null,也只有一个,值是可以有多个null
HashTable的应用非常广泛,HashMap是新框架中用来代替HashTable的类,Hashtable是Dictionary的子类,HashMap是Map接口的一个实现类;
HashMap HashTable
null可以为key 不可以
线程不同步 线程同步
不能用get判断是否存在某个键 有put 和 get方法
因为null本来是key
应该用containskey()方法来判断
集合难点基本就这些了。
接口和抽象类的概念应该都清楚了,主要是他们的区别以及在什么地方该用哪个是关键!
抽象类 abstract
1: 当一个类只有方法的定义,而没有实现这种情况下我们一般会做成抽象类
public abstract class Ab {
//无方法体--抽象方法
public abstract void print();
}
2: 当一个类有抽象方法,则本类必须声明为抽象类,反之,一个类声明为抽象类,未必有抽象方法.
3: 一个抽象类不能被实例化.
Ab a=new Ab();//错误
4: 抽象类需要子类去实现抽象方法.
5: 抽象类的子类如果不实现父类的抽象方法,则必须生明为抽象类
6:一般在设计的时候采用抽象类,象设计人员只关心宏观问题,而把方法的具体实现交给别人来做。
接口的特性 interface 实现类为implements
1:接口相当于一种特殊的抽象类,不能有具体实现,全部都是方法的定义,使一种完全抽象的类型。
2:接口与接口之间可以多继承。
3:类和接口之间可以多实现,也就是说一个类可以实现多个接口。
4:一个类可以即继承一个类,又可以实现多个接口。
5:抽象类也可以实现多个接口,抽象类可以不实现接口的方法。
6:如果实现了接口A,B两个接口具有同样的方法,则子类只实现一次就可以。
7:接口一般用途:是由设计人员在设计的过程中只关注宏观方法定义,而把具体方法的实现交给具体编码的人员。
8:接口中不能有构造函数,不能有变量,只能由static final(静态常量)。
9:接口可以抽象public abstract interface f{}
接口和抽象类之间的区别
1:接口不能有实现,而抽象类可以有具体的实现
2:接口被实现,而抽象类被继承
3:接口中只允许静态常量,而抽象类什么都允许
4:接口间可以多继承,而抽象类只能单继承
共同点:都不能实例化,都需要把方法交给子类去实现。
抽象类的使用场合
tercher类和student类都有相同实现步骤的方法save,实现的代码完全相同,此时我们可以从中抽取出save方法放到抽象类person的实现方法save中,
tercher,student都继承person类,这样可以减少代码的重用,person还有个hello方法是抽象方法,子类的实现方法不同都有自己的hello方法
接口的使用场合
当teacher,student等子类的所有共同方法实现都不同时候用接口给他们协定一个规则。
接口是一种协定,抽象类则相当于类模板。
使用抽象类,而不要使用接口来分离协定与实现。
如果需要提供多态层次结构的值类型,使用接口。
如果一个类型必须实现多个协定,或者协定适用于多种类型,使用接口。
虽然抽象类和接口都支持将协定与实现分离开来,但接口不能指定以后版本中的新成员,而抽象类可以根据需要添加成员以支持更多功能。
优先考虑定义类,而不是接口。
以上说明都理解了就可以很方面的用接口和抽象类了。
在看看4个类类型
1.内部类
类A中内部类B为静态的时候 它就看作是A类的静态方法
new A.b() 创建内部类的实例
1.静态的内部类 static inner class
第一: 跟普通方法评级的;
第二: 只能访问外部类的静态变量和静态方法;
第三: 外部类.内部类 对象名=new 外部类.内部类();
2.成员内部类 member inner classes
初始化的方式
Outer outer = new Outer(100,"qgl");
//因为是成员内部类,成员内部类的对象完全依赖外部类的对象,如果外部类的对象没有创建,则无法创建成员内部类的对象
Outer.Inner inner=outer.new Inner(30);
内部类可以调用外部类的成员变量
但是外部类不可以调用内部的.
System.out.println("outer.id="+Outer.this.id);
System.out.println("outer.name="+Outer.this.name);
3.本地内部类 local inner classes
在方法内部的临时类
只能访问final的本地常量
4.匿名内部类 anonymous inner classes
下面看看线程方面的知识:
首先搞明白多线程同步关键词synchronized
理解的关键就在一个对象一把锁,普通方法锁的是对象,静态方法锁的是类对象。如果多个对象之间同步需要定义一个公共属性,private final object。
以上是线程的状态图
sleep和wait的区别 面试题之一
sleep 即为暂停,恢复之后又主程序继续控制。
wait 为阻塞。必须有notify来通知才唤醒。
sleep是thread的方法。wait和notify,notifyAll 都是object的方法。
sleep不释放锁,wait会释放锁。
sleep必须捕获异常,并在任何地方都可以调用。
而wait和notify,notifyAll只有在同步块里用到。
线程启动的两种方式
public class T1 extends Thread {
public void run(){
System.out.print("run t1");
}
public static void main(String[] args) {
T1 t1=new T1();
t.1start();
}
}
public class T2 implements Runnable {
public void run(){
System.out.print("run T2");
}
public static void main(String[] args) {
T2 t2=new T2();
new Thread(t2).start();
}
}
问题:什么情况下使用Thread 什么情况下使用Runnable
1、通常情况下不需要修改线程类当中的除了run方法之外的其他方法的情况下使用实现Runnable接口
这样有2个好处:
一、如果本来已经继承了一个类而有需要线程化那么就必须实现Runnable接口
二、多个线程访问同一个资源的时候方便
反而继承了Thread的时候每个对象都拥有一个资源无法做到资源的共享
当然这些共能可以通过内部类来实现
下面看一个线程面试题:
要求:设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1.写出程序.
回答: 以下程序使用内部类实现线程,对j增减的时候没有考虑顺序问题.