1、java语言的三种技术架构
J2EE:企业版。是为开发企业环境下的应用程序提供的一套解决方案。
该技术体系中包含的技术如Servlet、Jsp等,主要针对于Web应用程序开发。
J2SE:标准版。是为开发普通桌面和商务应用程序提供的解决方案。
该技术体系是其他两者的基础,可以完成一些桌面应用程序的开发。比如Java版的扫雷。
J2ME:小型版。是为开发电子消费产品和嵌入式设备提供的解决方案。
该技术体系主要应用于小型电子消费类产品,如手机中的应用程序等。
JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性。
java语言是跨平台,jvm不是跨平台的。
JRE(Java Runtime Environment):java的运行环境,包括jvm+java的核心类库。
JDK(Java Development Kit):java的开发工具,包括jre+开发工具
通过Java语言编写的应用程序在不同的系统平台上都可以运行。
跨平台的原因:只要在需要运行java应用程序的操作系统上,先安装一个Java虚拟机(JVM Java Virtual Machine)即可。由JVM来负责Java程序在该系统中的运行。
(1) path是配置Windows可执行文件的搜索路径,即扩展名为.exe的程序文件所在的目录,用于指定DOS窗口命令的路径。
(2) Classpath是配置class文件所在的目录,用于指定类搜索路径,JVM就是通过它来寻找该类的class类文件的。
Public 公共的,所有类均可以访问。
Protected 受保护的,当前包和不同包子类可以访问。
Default 默认访问类型,当前包下可以访问。
Private 私有的,只有本类可以访问。
(1)&&会出现短路,如果可以通过第一个表达式判断出整个表达式的结果,则不继续后面表达式的运算;只能操作boolean类型数据;
(2)&不会出现短路,将整个表达式都运算。既可以操作boolean数据还可以操作数。
由数字(0-9),大小写英文字母,以及_和$组成。
不能以数字开头。不能使用关键字来自定义命名。
(1)基本数据类型(4类8种):
整数类型:byte(1)、short(2)、int(4)、long(8)
浮点数类型:float(4)、double(8)
字符类型:char(2)
布尔类型:boolean(ture false)(4)
(2)引用数据类型: 类、接口、数组
自动装箱:new Integer(6);底层调用Integer.valueOf(6)
自动拆箱:int i = new Integer(6);,底层调用i.intValue();方法实现。
精度从高到低 double float long int short(char) byte
(1)自动类型转换 将一个低精度---à高精度
(2)强制类型转换 将一个高精度---à低精度(精度会下降)
使用Bigdecimal类进行浮点型数据的运算
可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。
在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。
使用标签和break,并且应在所在循环的外层循环之前定义标签:
在idk 1.7之前,switch只能支持byte, short, char, int或者其对应的封装类以及Enum类型。从idk 1.7之后switch开始支持String。switch中的参数不支持其他类型,例如long类型
对于short s1 = 1; s1 = s1 + 1; 由于s1+1运算时会自动提升表达式的类型,所以结果是int型,再赋值给short类型s1时,编译器将报告需要强制转换类型的错误。
对于short s1 = 1; s1 += 1;由于 += 是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译。
System.out.println(((1<3)?"a":"b")+3+4); System.out.println(((1<3)?"a":"b")+(3+4));
控制台: a34 a7
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
都是用来存储字符串的,它们保存方式不一样,char有固定的长度,而varchar属于可变长的字符类型。
char类型是字符型 16个位 两个字节 没有符号位 16个位全部都是数值位,所以取值范围0到2的16次方-1
byte类型是整数类型当中的一种 8个位 一个字节 其中1个符号位 15个数值位,所以取值范围-2的15次方到+2的15次方-1
1.声明方式不同:基本类型不使用new关键字,而包装类型需要使用new关键字。
2.存储方式及位置不同:基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用;
3.初始值不同:基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null;
4.使用方式不同:基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到。
因为将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8只要将其左移3位即可,而位运算cpu直接支持的,效率最高,所以,2乘以8等於几的最效率的方法是2 << 3。
对于for循环就是调用get(i)取得元素,而对于foreach是通过iterator实现的遍历,
对于数组来说,for和foreach循环效率差不多,对于链表来说,for循环效率明显比foreach低。
对于ArrayList来说,它是通过一个数组实现的,可以随机存取;但是LinkedList是通过链表实现的,for循环时要获取第i个元素必须从头开始遍历,而iterator遍历就是从头开始遍历,遍历完只需要一次,所以for循环需要的时间远远超过for循环。
静态实例化:创建数组的时候已经指定数组中的元素, int[] a=new int[]{1,3,3}
动态实例化:实例化数组的时候只指定了数组程度,数组中所有元素都是数组类型默认值
1、简单的值类型的数组,每个数组成员是一个引用(指针),引用到栈上的空间(因为值类型变量的内存分配在栈上)
2、引用类型,类类型的数组,每个数组成员仍是一个引用(指针),引用到堆上的空间(因为类的实例的内存分配在堆上)
值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身,当在一个方法操作参数的时候,其实操作的是引用所指向的对象。
一般认为,java内的传递都是值传递,java中实例对象的传递是引用传递 。
传引用是指传递的是地址而不是值本身,传值则是传递值的一份拷贝。
改变了,因为传递是对象的引用,操作的是引用所指向的对象
不能,数组一旦实例化,它的长度就是固定的
创建一个新数组,从后到前循环遍历每个元素,将取出的元素依次顺序放入新数组中
1).采用new 2).通过反射 3).采用clone 4).通过序列化机制
前2者都需要显式地调用构造方法。造成耦合性最高的恰好是第一种,因此你发现无论什么框架,只要涉及到解耦必先减少new的使用。
26、变量有什么用?为什么要定义变量?什么时候用?
变量的作用:用来存储数据。
为什么要定义变量:用来不断的存放同一类型的常量,并可以重复使用
26、成员变量和局部变量的区别(重点)
(1)作用域
成员变量:针对整个类有效。
局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)
(2)存储位置
成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。
当方法调用完,或者语句结束后,就自动释放。
(3)初始值
成员变量:有默认初始值。
局部变量:没有默认初始值,使用前必须赋值。
27、静态变量和实例变量的区别?
语法定义上:静态变量前要加static关键字,而实例变量前则不加。
程序运行时:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
28、全局变量和局部变量哪个能被覆盖
Java当中不允许定义全局变量。Java当中不存在全局变量的概念,只有类体当中的成员变量(实例变量)和方法体当中的局部变量。覆盖是只有方法才存在的。子类继承得到父类方法后对其进行重新实现叫做方法覆盖.属性根本不存在覆盖。
29、静态方法和实例方法的区别主要体现在两个方面:
在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有使用"对象名.方法名"。
调用静态方法可以无需创建对象。静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式。私有是封装的一种体现。用private关键字修饰。
继承: 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。用关键字extends
多态: 一个对象在程序不同运行时刻代表的多种状态,父类或者接口的引用指向子类对象。封装和继承是为多态做准备的。多态实现的机制是重写和重载。
多态前提:A:存在继承或者实现关系B:有方法的重写C:父类(接口)引用指向子类(实现)对象
实现多态的技术为动态绑定。在执行期间判断所引用对象的实际类型,根据实际类型调用其相应方法。多态允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象不同采用多种不同的行为方式。(发送消息就是方法调用)。
多态作用:消除了类型之间的耦合关系。
动态绑定技术(dynamic binding),执行期间判断所引用对象的实际类型,根据实际类型调用对应的方法.
重载:在同一类中。方法名相同,参数列表不同。重载可以改变返回类型。是一个类中多态性的体现。
重写:在不同类中(子父类中)。是父类和子类之间多态性体现。重写时访问控制权限不能更加严格,抛出异常(非运行时异常)种类不能更加规范。
1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
2)抽象类中成员变量可以是各种类型的,而接口中成员变量只能是public static final类型;
3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
5)抽象类中定义的是继承体系中的共性功能。接口中定义的是继承体系中的扩展功能。
6)抽象类被继承是"is a"关系:xx是yy的一种。接口被实现是"like a"关系:xx像yy的一种。
7)抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。
1、通过接口可以实现不相关类的相同行为,而不需要了解对象所对应的类。
2、通过接口可以指明多个类需要实现的方法。
3、通过接口可以了解对象的交互界面,而不需了解对象所对应的类。
另:Java是单继承,接口可以使其实现多继承的功能。
Fu f = new Zi();
A:成员变量 编译看左边,运行看左边
B:成员方法 编译看左边,运行看右边
C:静态方法 编译看左边,运行看左边
36、构造器Constructor是否可被override?
构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload。
37、接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? 抽象类中是否可以有静态的main方法?
接口可以继承接口,接口间继承用extends。抽象类可以实现(implements)接口,但接口不能实现抽象类,抽象类间也可以用extends。抽象类可以继承实体类,但前提是必须有无参的构造方法。抽象类中可以有静态的main方法。
抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。
38、构造方法能不能重载?能不能重写?可以重载,必须重写
必须重写
40、创建一个子类对象的时候,那么父类的构造方法会执行吗?
会执行。当创建一个子类对象,调用子类构造方法的时候,子类构造方法会默认调用父类的构造方法。
41、写clone()方法时,通常都有一行代码,是什么?
clone 有缺省行为,super.clone();因为首先要把父类中的成员复制到位,然后才是复制自己的成员。
42、内部类可以引用它的包含类的成员吗?有没有什么限制?
完全可以。如果不是静态内部类,那没有什么限制!
如果你把静态嵌套类当作内部类的一种特例,那在这种情况下不可以访问外部类的普通成员变量,而只能访问外部类中的静态成员,
可以继承其他类或实现其他接口。不仅是可以,而是必须!
能正常编译,但运行的时候会提示”main方法不是public的”。
程序能正常编译。运行时会抛NoSuchMethodError异常。
全局变量是全局可见的,Java不支持全局可见的变量,因为:全局变量破坏了引用透明性原则。全局变量导致了命名空间的冲突。
49、父类的静态方法能否被子类重写 不能
子类继承父类后,有相同的静态方法和非静态,这是非静态方法覆盖父类中的方法(即方法重写),父类的该静态方法被隐藏(如果对象是父类则调用该隐藏的方法);
另外子类可继承父类的静态与非静态方法,至于方法重载我觉得它其中一要素就是在同一类中,不能说父类中的什么方法与子类里的什么方法是方法重载的体现。
50、如何把一段逗号分割的字符串转换成一个数组?
1.用正则表达式,代码大概为:String [] result = orgStr.split(“,”);
2.用StingTokenizer ,代码为:StringTokenizer tokener = StringTokenizer(orgStr,”,”);
51、数组有没有length()这个方法? String有没有length()这个方法?
数组没有length()这个方法,有length的属性。String有length()这个方法。
52、什么是不可变对象
不可变对象指对象一旦被创建,状态就不能再改变。
任何修改都会创建一个新的对象,如 String、Integer及其它包装类。
53、能否创建一个包含可变对象的不可变对象?
当然可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。
最常见的例子就是对象中包含一个日期对象的引用.
54、String对象的intern()方法
intern()方法会首先从常量池中查找是否存在该常量值,如果常量池中不存在则现在常量池中创建,如果已经存在则直接返回。
toString(),equals,hashCode,getClass,finalize,clone, wait(),notify,notifyAll
56、String类中方法有?
和长度有关的方法 int length() 得到一个字符串的字符个数
和数组有关的方法
byte[] getBytes() 将一个字符串转换成字节数组
char[] toCharArray() 将一个字符串转换成字符数组
String[] split(String) 将一个字符串按照指定内容劈开
和判断有关的方法
boolean equals(String) 判断两个字符串的内容是否一模一样
boolean equalsIgnoreCase(String) 忽略大小写的比较两个字符串的内容是否一模一样 boolean contains(String) 判断一个字符串里面是否包含指定的内容
Boolean startsWith(String) 判断一个字符串是否以指定的内容开头
boolean endsWith(String) 判断一个字符串是否以指定的内容结尾
compareTo(String) 对字符串内容按字典顺序进行大小比较,通过返回的整数值指明当前字符串与参数字符串的大小关系。若当前对象比参数大则返回正整数,反之返回负整数,相等返回0。
compareToIgnore(String) 与compareTo方法相似,但忽略大小写。
和改变内容有关的方法
String toUpperCase() 将一个字符串全部转换成大写
String toLowerCase() 将一个字符串全部转换成小写
String replace(String,String) 将某个内容全部替换成指定内容
String replaceAll(String,String) 将某个内容全部替换成指定内容,支持正则
String repalceFirst(String,String) 将第一次出现的某个内容替换成指定的内容
String substring(int) 从指定下标开始一直截取到字符串的最后
String substring(int,int) 从下标x截取到下标y-1对应的元素
String trim() 去除一个字符串的前后空格
和位置有关的方法
char charAt(int) 得到指定下标位置对应的字符
int indexOf(String) 得到指定内容第一次出现的下标
int lastIndexOf(String) 得到指定内容最后一次出现的下标
字符串连接 concat(String) 连接到当前字符串的后面,效果等价于"+"
字符串与基本类型的转换
static short parseInt(String) parseLong(String) parseFloat(String)
parseDouble(String s) parseByte(String) parseShort(String)
基本类型转换为字符串类型
static String valueOf(boolean b) valueOf(char c) valueOf(int i)
valueOf(long l) valueOf(float f) valueOf(double d)
58、能不能自己写个类,也叫java.lang.String?
public class String {
public static void main(String[] args) {
System.out.println("string");
}
}
可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载jre.jar包中的那个java.lang.String。由于在tomcat的web应用程序中,都是由webapp自己的类加载器先自己加载WEB-INF/classess目录中的类,然后才委托上级的类加载器加载,如果我们在tomcat的web应用程序中写一个java.lang.String,这时候Servlet程序加载的就是我们自己写的java.lang.String,但是这么干就会出很多潜在的问题,原来所有用了java.lang.String类的都将出现问题。
59、使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。
60、说出一些常用的类,包,接口,请各举5个
常用的类:BufferedReader,BufferedWriter,FileReader,FileWirter,String,Integer,java.util.Date,System,Class,List,HashMap
常用的包:java.lang,java.io,java.util,java.sql,javax.servlet,org.apache.strtuts.action,org.hibernate
常用的接口:Remote,List,Map,Document,NodeList,Servlet,HttpServletRequest,HttpServletResponse,Transaction(Hibernate),Session(Hibernate),HttpSession
61、final,finally,finalize区别。
final是最终的意思。它可以用于修饰类,成员变量,成员方法。
它修饰的类不能被继承,它修饰的变量时常量,它修饰的方法不能被重写。
finally:是异常处理里面的关键字。
它其中的代码永远被执行。特殊情况:在执行它之前jvm退出。System.exit(0);
finalize:是Object类中的一个方法。
它是于垃圾回收器调用的方式。
62、final有哪些用法?
1).被final修饰的类不可以被继承
2).被final修饰的方法不可以被重写
3).被final修饰的变量不可以被改变。
如果修饰的引用,那么表示引用不可变,引用指向的内容可变。
4).被final修饰的方法,JVM会尝试将其内联,以提高运行效率 。
5).被final修饰的常量,在编译阶段会存入常量池中。
当前回答出编译器对final域要遵守的两个重排序规则更好:
1).在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
2).初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。
63、String、StringBuffer、StringBuilder 的区别是什么?
1、String:底层使用一个不可变的字符数组private final char value[];
它内容不可变。适用于少量的字符串操作的情况
stringBuffer
和StringBuilder
都继承了AbstractStringBuilder
底层使用的是可变字符数组:char[] value;
内容是可变的
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
2、运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String。
3、StringBuilder是线程不安全的,效率较高;而StringBuffer是线程安全的,效率较低。通过他们的append()方法来看,StringBuffer是有同步锁,而StringBuilder没有:
d. 1.字符串的底层都是基于char类型的数组实现的
2.当在字符串后面不断的追加新内容的时候,使用StringBuffer效率高,因为对于StringBuffer的对象来说底层开辟的数组对象会预留16块空间,当我们不断的在字符串的后面追加新的内容,他是在缓冲区里面追加,当缓冲区满了那么才开辟一个新的数组对象,然后再预留当前数组空间大小一半的空间当作缓冲区.
3.对于一个String类型的对象来说,没有缓冲区的概念,当前字符串有多少个字符,底层数组就开辟多大空间,如果我们想在String类型的后面不断的追加新的内容的话,底层其实不断的在创建新的数组对象,相对来说创建数组的频率会高很多,效率较低。因为每次开辟新数组之后都需要复制老元素 改变引用指向 然后将来还要回收旧的数组...
64、判断两个对象是否相同,能使用equlas比较吗?
不能。Equlas大多用来做字符串比较,要判断基本数据类型或者对象类型,需要使用==
65、==与equals有什么区别?
==可以判断基本数据类型值是否相等,也可以判断两个对象指向的内存地址是否相同,也就是说判断两个对象是否是同一个对象,Equals通常用来做字符串比较。
==是运算符,用于比较两个值是否相等,而equals是Object类的方法,用于比较两个对象是否相等。默认Object类的equals方法是比较两个对象的内存地址,此时和==的结果一样,如果需要比较对象内容,需要重写equal方法。
66、equals()和hashcode()的区别
hashCode()是Object类的一个方法,返回一个哈希值。
如果两个对象根据equals()方法比较相等,那么调用这两个对象中任意一个对象的hashCode()方法必须产生相同的哈希值。
如果两个对象根据equals()方法比较不相等,那么产生的哈希值不一定相等(碰撞的情况下还是会相等的)。
String 不可变是因为在 JDK 中 String 类被声明为一个 final 类,且类内部的 value 字节数组也是 final 的,只有当字符串是不可变时字符串池才有可能实现,字符串池的实现可以在运行时节约很多 heap 空间,因为不同的字符串变量都指向池中的同一个字符串;如果字符串是可变的则会引起很严重的安全问题,譬如数据库的用户名密码都是以字符串的形式传入来获得数据库的连接,或者在 socket 编程中主机名和端口都是以字符串的形式传入,
String类是final类故不可以继承。
因为 String 是不可变的,一旦创建就不能更改,直到垃圾收集器将它回收才能消失,即使我们修改了原先的变量,实际上也是在内存中新建一个对象,原数据还是保留在内存中等待回收;而字符数组 char[] 中的元素是可以更改的,也就是说像密码等保密信息用完之后我们可以马上修改它的值而不留痕迹,从而相对于 String 有更好的安全性。
69、继承collection接口的:list,set,queue,sortedSet.
继承Map接口的:
HashMap:无序存放,Key不重复,
HashTable:无序存放,Key不重复,
TreeMap:按Key排序,Key可重复,
IdentityHashMap:弱引用Map集合,
List 是集合列表接口,ArrayList 和 LinkedList 都是 List 接口的实现类。
1、ArrayList是object类型出始容量为10的,基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的),支持随机访问。
因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。
2、LinkedList双向循环链表的数据结构, 同时实现了双端队列 Deque 接口,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势,不支持随机访问,使用下标访问一个元素。
ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。
适用场景分析:
当需要对数据进行对此访问的情况下选用ArrayList,
当需要对数据进行多次增加删除修改时采用LinkedList。
70、HashMap 和 Hashtable 的区别?
HashMap 继承自 AbstractMap,Hashtable 继承自 Dictionary 类,两者都实现了 Map 接口;
1、对于null的处理不同
HashMap无论是主键还是值都能存放null,但是由于主键要求唯一,所以主键只能存放一个null,但是值能存放多个空。
Hashtable无论是主键还是值都不能添加null,会触发空指针异常。
2、同步特性不同
HashMap同一时间允许多个线程同时进行访问,线程不安全,效率相对较高,但是可能出现并发错误。
Hashtable底层大量的使用了synchronized修饰方法,同一时间只允许一个线程对其操作,线程安全,效率相对较低,但是不会出现并发错误。
从jdk5.0开始集合的工具类Collections当中出现了一个静态方法synchronizedMap方法可以将一个不安全的map变成线程安全的。
在高并发的场景下推荐使用java.util.concurrent.ConcurrentHashMap 有更高的性能
3、底层实现的区别
HashMap分组组数可以指定,默认分为16个小组,但是最终的分组组数一定是2的n次方数,在计算散列的时候,底层使用&(分组组数-1) [按位与运算]
Hashtable分组组数可以随意指定,默认分11组,可以随意指定分组模分组组数
4、出现的版本不同 HashMap jdk1.2 Hashtable jdk1.0
5、迭代器不同
HashMap迭代器(Iterator)是fail-fast迭代器,Hashtable的enumerator迭代器不是fail-fast
什么是fail-fast? 就是最快的时间能把错误抛出而不是让程序执行。
Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
ConcurrentHashMap将整个Map分为N个segment(类似HashTable),可以提供相同的线程安全,但是效率提升N倍,默认N为16。
HashMap可以通过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap);
synchronize锁住整个Hashtable对象,避免在你取count的时候有其他线程修改这个Hashtable的count值。
取交集:listA.retainAll(listB);
取并集:1.listA.removeAll(listB); 2.listA.addAll(listB);
取差集:listA.removeAll(listB);
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
使用for循环.增强for循环foreach、Iterator遍历集合时,不能对集合做添加和删除操作:
1.在遍历时往数组增加数据,会导致遍历不完整(因为增加了新成员长度变了),或者死循环(因为总是有新的进去)。
2.在遍历时删除数据,则会导致数组访问越界(因为长度缩短了,指针指向了一个已经标示为空的区域)否则会引发ConcurrentModificationException异常。
当在 ArrayList 中增加一个对象时 Java 会去检查 Arraylist 以确保已存在的数组中有足够的容量来存储这个新对象(默认为 10,最大容量为 int 上限,减 8 是为了容错),如果没有足够容量就新建一个长度更长的数组(原来的1.5倍),旧的数组就会使用 Arrays.copyOf 方法被复制到新的数组中去,现有的数组引用指向了新的数组。
ArrayList 在小于扩容容量的情况下其实增加操作效率是非常高的,在涉及扩容的情况下添加操作效率确实低,删除操作需要移位拷贝,效率是低点。因为 ArrayList 中增加(扩容)或者是删除元素要调用 System.arrayCopy 这种效率很低的方法进行处理,所以如果遇到了数据量略大且需要频繁插入或删除的操作效率就比较低了,具体可查看 ArrayList 的 add 和 remove 方法实现,但是 ArrayList 频繁访问元素的效率是非常高的,因此遇到类似场景我们应该尽可能使用 LinkedList 进行替代效率会高一些。
Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型;Array 的大小是固定的,ArrayList 的大小是动态变化的;ArrayList 提供了更多的方法和特性,譬如 addAll()、removeAll()、iterator() 等。
注意:容器类提供的迭代器都会在迭代中间进行结构性变化检测,如果容器发生了结构性变化,就会抛出 ConcurrentModificationException,所以不能在迭代中间直接调用容器类提供的 add、remove 方法,如需添加和删除,应调用迭代器的相关方法。
list.subList(
30
,
50
).clear();
相同点为都是 AbstractList 的子类,都可以序列化。
区别为 Collections.emptyList() 返回一个不可变的 List,而new ArrayList() 返回的 List 是可变的。只有当确实需要返回一个不可变空 List 时才使用 Collections.emptyList(),多次调用 Collections.emptyList() 只会返回同一个 List实例,而多次调用 new ArrayList() 每次都会返回新的实例。对于 emptySet() 和 emptyMap() 等类似方法有同样的结论适用。
ArrayList 比 LinkedList 使用普通 for 循环遍历快,
ArrayList 的 get 方法只是通过数组下标从数组里面拿一个元素而已,所以 get 方法的时间复杂度是 O(1) 常数,和数组的大小没有关系,只要给定数组的位置就能定位到数据,而 Java 中 ArrayList 是基于数组实现的,数组是内存地址连续的空间,取值其实是地址的偏移,所以自然块的多。
LinkedList 是双向链表,所以 node 方法里面的 if 判断条件是在计算 index 在前一半还是后一半,然后决定是前序遍历还是后序遍历,这样二分法遍历查找会快很多,但是在 for 循环里面 LinkedList 在 get 任何一个位置元素时都会把前面的元素走一遍,随着 LinkedList 的容量越大这个遍历也就越大,所以其遍历的时间复杂度为 O(N/2),N 为 LinkedList 的容量,所以自然效率就比较低了。
ArrayList是直接通过数组来进行存储,所以在使用下标的方式循环遍历的时候性能最好,通过下标可以直接取数据,速度最快,而且由于 ArrayList 是实现了 RandomAccess 接口(随机存取),这个接口是一个标记接口,表明了 ArrayList 集合的数据元素之间没有关联,位置间没有索引依赖。而如果对于 ArrayList 使用 for-each 或者迭代器进行遍历就没有 index 索引遍历效率高了,因为迭代器强制将 RandomAccess 的 ArrayList 建立了前后遍历关系,且在每次遍历过程中进行了一堆判断,所以相对来说对于 ArrayList 来说遍历使用普通 index 比迭代器要效率高些,但是差距不是十分明显。
LinkedList其为双向链表的实现存储,前后元素是通过链表索引建立关联的,无法直接取到对应的下标,因此在使用普通的 index 索引下标遍历时就需要计算对应的元素在哪,二分法决定头部还是尾部遍历,然后一步步的遍历找到元素,所以在遍历中每次都要从头查找元素位置,十分低效率。而迭代器的实现就是指向下一个元素,迭代器直接通过 LinkedList 的指针进行遍历,一次遍历就能找到每个合适的元素,所以 LinkedList 在使用迭代器遍历时效率最高。
总之就是对于不同数据结构实现的集合列表就应该选择不同的遍历方式,尤其是 LinkedList 一定不要使用普通索引下标方式遍历,其效率极低。
首先堆栈是先进后出,队列是先进先出,LinkedList 不但实现了 List 接口还实现了 Deque 双端队列接口,接着给出实现如下。
LinkedList 是以双向链表实现,链表无容量限制(但是双向链表本身需要消耗额外的链表指针空间来操作),其内部主要成员为 first 和 last 两个 Node 节点,在每次修改列表时用来指引当前双向链表的首尾部位,所以 LinkedList 不仅仅实现了 List 接口,还实现了 Deque 双端队列接口(该接口是 Queue 队列的子接口),故 LinkedList 自动具备双端队列的特性,当我们使用下标方式调用列表的 get(index)、set(index, e) 方法时需要遍历链表将指针移动到位进行访问(会判断 index 是否大于链表长度的一半决定是首部遍历还是尾部遍历,访问的复杂度为 O(N/2)),无法像 ArrayList 那样进行随机访问。(如果i>数组大小的一半,会从末尾移起),只有在链表两头的操作(譬如 add()、addFirst()、removeLast() 或用在 iterator() 上的 remove() 操作)才不需要进行遍历寻找定位。具体感兴趣可以去看下 LinkedList 的源码。
每次扩容的时候分组组数会直接*2 也就是变成原本的2倍大小 例如默认16组那么扩容一次之后变成32组
首先HashMap构造方法可以传入两个参数,分别是int类型的分组组数和float类型的加载因子
而其扩容跟阈值有关 只是在不同的JDK版本当中HashMap的实现也有区别.
所谓阈值 = 分组组数*加载因子 = 这是HashMap达到扩容条件的最小临界值
*:当然在不同的JDK7.0当中,即便达到或者超过阈值,如果新元素要去的小组不为空,也会暂时不扩容
以默认的分组组数16和加载因子0.75F为例 也就是Map
那么阈值就是12 也就是第13个元素添加的时候可能会导致扩容,那么底层将从分16组变成分32组 然后所有元素进行重新散列
毫无疑问,重新散列会消耗时间,在使用HashMap的时候我们应该让分组组数*加载因子>总共要存放的键值对总量
以避免添加过程当中触发扩容操作
考你分组组数和加载因子
我们可以让HashMap底层开辟更多的分组,然后在元素总量不变的情况下,分组组数越多每一组元素个数自然越少,效率自然越高。
HashMap构造方法是可以传参数来指定分组组数和加载因子(又名装填因子)的,
这两者相乘得到阈值也就是达到扩容条件的最小临界值
我们使用HashMap应该在知道元素总量的情况下保证分组组数*加载因子>元素总量 从而避免添加过程当中触发扩容操作影响效率
另外在这个条件成立的前提下只需要
分组组数越大,数据将被散列到越多个小组,查找效率越高
加载因子越小,扩容会发生的越早,也就是在尽可能的保证效率
构造方法的两个参数:int 分组组数 , float 加载因子
在加载因子不变的情况下 如果我们让分组组数变大
则效率更高 -> 但是需要更多的空间
在分组组数不变的情况下 如果让加载因子变大
则更加节省空间 -> 但是效率更低
*:我们在已经知道要存放的元素个数的前提下 应该让
分组组数*加载因子 > 元素总量 避免添加过程当中触发扩容操作
Node
table:Node
size:记录了放入HashMap的元素个数。
loadFactor:负载因子(缺省值为0.75)。
threshold:阈值,决定了HashMap何时扩容,以及扩容后的大小,一般等于table大小乘以loadFactor。
简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。
当向容器添加元素的时候,会判断当前容器(table数组)的元素个数,如果大于等于阈值,即当前数组的长度乘以加载因子(缺省值为0.75)的值的时候,就要自动扩容啦。
1 HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
2 HashMap的数据结构:在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根绝hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上.
HashMap是一个针对数据结构的键值,每个键都会有相应的值,关键是识别这样的值。
HashMap 基于 hashing 原理,我们通过 put ()和 get ()方法储存和获取对象。当我们将键值对传递给 put ()方法时,它调用键对象的 hashCode ()方法来计算 hashcode,让后找到 bucket 位置来储存值对象。当获取对象时,通过键对象的 equals ()方法找到正确的键值对,然后返回值对象。HashMap 使用 LinkedList 来解决碰撞问题,当发生碰撞了,对象将会储存在 LinkedList 的下一个节点中。 HashMap 在每个 LinkedList 节点中储存键值对对象。
当我们需要一个同步的HashMap时,有两种选择:
1、使用Collections.synchronizedMap(..)来同步HashMap。
2、使用ConcurrentHashMap的
这两个选项之间的首选是使用ConcurrentHashMap,这是因为我们不需要锁定整个对象,以及通过ConcurrentHashMap分区地图来获得锁。
HashMap基于哈希表的 Map 接口的实现。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。HashMap中hash数组的默认大小是16,而且一定是2的指数。Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。HashMap 实现 Iterator,支持fast-fail。
哈希表是由数组+链表组成的,它是通过把key值进行hash来定位对象的,这样可以提供比线性存储更好的性能。HashMap不是线程安全的。
list:有序的 可重复的 访问:可以for循环,foreach循环,iterator迭代器 迭代。
set:无序的 不重复的 访问:可以foreach循环,iterator迭代器 迭代
map:存储的是一对一对的映射 ”key=value“,key值 是无序,不重复的。value值可重复
访问:可以map中key值转为为set存储,然后迭代这个set,用map.get(key)获取value,也可以 转换为entry对象 用迭代器迭代
Vector 是线程安全的动态数组,同 ArrayList 一样继承自 AbstractList 且实现了 List、RandomAccess、Cloneable、Serializable 接口,内部实现依然基于数组,Vector 与 ArrayList 基本是一致的,唯一不同的是 Vector 是线程安全的,会在可能出现线程安全的方法前面加上 synchronized 关键字,其和 ArrayList 类似,随机访问速度快,插入和移除性能较差(数组原因),支持 null 元素,有顺序,元素可以重复,线程安全。
Stack 是继承自 Vector 基于动态数组实现的线程安全栈,不过现在已经不推荐使用了,Stack 是并发安全的后进先出,实现了一些栈基本操作的方法(其实并不是只能后进先出,因为继承自 Vector,所以可以有很多操作,严格说不是一个栈)。
其共同点都是使用了方法锁(即 synchronized)来保证并发安全的。
ArrayList 在默认数组容量不够时默认扩展是 1.5 倍,Vector 在 capacityIncrement 大于 0 时扩容 capacityIncrement 大小,否则扩容为原始容量的 2 倍。具体感兴趣可以去看二者源码。
1、Vector的方法都是同步的,是线程安全的,而ArrayList的方法不是,由于线程的同步必然要影响性能。因此,ArrayList的性能比Vector好。
2、当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小。 ArrayList就有利于节约内存空间。
3、大多数情况不使用Vector,因为性能不好,但是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性。
4、Vector可以设置增长因子,而ArrayList不可以。
适用场景分析:
1、Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。
2、如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。
因为 Vector 实现并发安全的原理是在每个操作方法上加锁,这些锁并不是必须要的,在实际开发中一般都是通过锁一系列的操作来实现线程安全,也就是说将需要同步的资源放一起加锁来保证线程安全,如果多个 Thread 并发执行一个已经加锁的方法,但是在该方法中又有 Vector 的存在,Vector 本身实现中已经加锁了,双重锁会造成额外的开销,即 Vector 同 ArrayList 一样有 fail-fast 问题(即无法保证遍历安全),所以在遍历 Vector 操作时又得额外加锁保证安全,还不如直接用 ArrayList 加锁性能好,所以在 JDK 1.5 之后推荐使用 java.util.concurrent 包下的并发类。此外 Vector 是一个从 JDK1.0 就有的古老集合,那时候 Java 还没有提供系统的集合框架,所以在 Vector 里提供了一些方法名很长的方法(如 addElement(Object obj),实际上这个方法和 add(Object obj) 没什么区别),从 JDK1.2 以后 Java 提供了集合框架,然后就将 Vector 改为实现 List 接口,从而导致 Vector 里有一些重复的方法。
如果需要使 Map 线程安全,大致有这么四种方法:
1、使用 synchronized 关键字,代码如下
synchronized(anObject) {
value = map.get(key);
}
2、使用 JDK1.5提供的锁(java.util.concurrent.locks.Lock)。代码如下
lock.lock();
value = map.get(key);
lock.unlock();
3、使用 JDK1.5 提供的读写锁(java.util.concurrent.locks.ReadWriteLock)。代码如下
rwlock.readLock().lock();
value = map.get(key);
rwlock.readLock().unlock();
这样两个读操作可以同时进行,理论上效率会比方法 2 高。
4、使用 JDK1.5 提供的 java.util.concurrent.ConcurrentHashMap 类。该类将 Map 的存储空间分为若干块,每块拥有自己的锁,大大减少了多个线程争夺同一个锁的情况。代码如下
value = map.get(key); //同步机制内置在 get 方法中
比较:
1、不同步确实最快,与预期一致。
2、四种同步方式中,ConcurrentHashMap 是最快的,接近不同步的情况。
3、synchronized 关键字非常慢,比使用锁慢了两个数量级。如果需自己实现同步,则使 用 JDK1.5 提供的锁机制,避免使用 synchronized 关键字。
98、不知道一个map集合的大小的情况下,怎么把它遍历出来(或者问Map是怎么取值的)
import java.util.*;
public class Test{
public static void main(String[] args){
Map
Set
for(Object key:set){
System.out.println("遍历map key="+key+" value"+map.get(key));
}
Collection
for(Object value:coll){
System.out.println("遍历map 这种方式不方便拿key value"+value);
}
Set
for(Map.Entry
System.out.println("遍历map key="+entry.getKey()+" value"+entry.getValue());
}
}
}
底层采用分段的数组+链表实现,线程安全
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是volatile的,也能保证读取到最新的值。)
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,分段扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容。
就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。
ConcurrentHashMap是使用了锁分段技术来保证线程安全的:
首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。
ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。
ConcurrentHashMap是使用了锁分段技术来保证线程安全,将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶,效率提升16倍。
它们都可以用于多线程的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。因为ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。
快速失败的Java迭代器可能会引发ConcurrentModifcationException在底层集合迭代过程中被修改。故障安全作为发生在实例中的一个副本迭代是不会抛出任何异常的。快速失败的故障安全范例定义了当遭遇故障时系统是如何反应的。例如,用于失败的快速迭代器ArrayList和用于故障安全的迭代器ConcurrentHashMap。
ConcurrentHashMap被作为故障安全迭代器的一个实例,它允许完整的并发检索和更新。当有大量的并发更新时,ConcurrentHashMap此时可以被使用。这非常类似于Hashtable,但ConcurrentHashMap不锁定整个表来提供并发,所以从这点上ConcurrentHashMap的性能似乎更好一些。所以当有大量更新时ConcurrentHashMap应该被使用。
Java BlockingQueue是一个并发集合util包的一部分。BlockingQueue队列是一种支持操作,它等待元素变得可用时来检索,同样等待空间可用时来存储元素。
LinkedList和ArrayList是另个不同变量列表的实现。ArrayList的优势在于动态的增长数组,非常适合初始时总长度未知的情况下使用。LinkedList的优势在于在中间位置插入和删除操作,速度是最快的。
LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
ArrayList实现了可变大小的数组。它允许所有元素,包括null。 每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
ArrayMap是Android SDK中提供的,非Android开发者可以略过. ArrayMap是用两个数组来模拟map,更少的内存占用空间,更高的效率.
1.TreeSet 是二叉树实现,Treeset中的数据是自动排序,不允许放入null值 。
2.HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束 。
3.HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例。
适用场景分析:
HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。
1、HashMap不是线程安全的,而ConcurrentHashMap是线程安全的。
2、ConcurrentHashMap采用锁分段技术,将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段segment,然后再在这个片段上面进行插入,而且这里还需要获取segment锁。
3、ConcurrentHashMap让锁的粒度更精细一些,并发性能更好。
IdentityHashMap是Map接口的实现。不同于
HashMap的,这里采用参考平等。
在HashMap中如果两个元素是相等的,则key1.equals(key2)
在IdentityHashMap中如果两个元素是相等的,则key1 == key2
Set set =new HashSet() 而不是Set set = new Set()
Map map = new HashMap() 而不是HashMap map = new HashMap()
List list = new ArrayList() 而不是ArrayList list = new ArrayList();
1.Set,List,map是接口,不能实例化,只能实例化接口实现类,HashSet,ArrayList,HashMap
2.实现类中的属性和方法set,map,list不能调用,只能调用接口本身属性和方法
3.接口有多个实现类,便于代码的重构,以后只需改实现类,其他可不变
List list = new ArrayList(); String[] str = (String[]) list.toArray(new String[list.size()])
String [] str = {"1","1","1","1"}; List list = Arrays.asList(str);
Collection是集合类的上级接口,继承与他的接口主要有Set和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
java.util.Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,在 Java 类库中有很多具体的实现,意义是为各种具体的集合提供最大化的统一操作方式。 譬如 Collection 的实现类有 List、Set 等,List 的实现类有 LinkedList、ArrayList、Vector 等,Vector 的实现类有 Stack 等,不过切记 Map 是自立门户的,其提供了转换为 Collection 的方法,但是自己不是 Collection 的子类。
java.util.Collections 是一个包装类,它包含有各种有关集合操作的静态多态方法,此类构造 private 不能实例化,就像一个工具类,服务于 Java 的 Collection 框架,其提供的方法大概可以分为对容器接口对象进行操作类(查找和替换、排序和调整顺序、添加和修改)和返回一个容器接口对象类(适配器将其他类型的数据转换为容器接口对象、装饰器修饰一个给定容器接口对象增加某种性质)。
Set里的元素是不能重复的,重复的元素将不会加到里面。通过Iterator()方法来区分是否重复,iterator()返回set中元素的迭代器,具体的set实现类依赖添加对象的equals()方法来检查等同性。元素重复与否是使用equals()方法进行判断的。
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
首先 Queue 是一种模拟 FIFO 队列的数据结构,新元素插入(offer)到队列的尾部,访问元素(poll)返回队列头部,一般队列不允许随机访问队列中的元素。Queue 接口主要定义了如下几个方法:
//
将指定元素加入队列尾部
void
add(
Object
e);
//
获取队列头部元素,但是不删除该元素,如果队列为空抛出
NoSuchElementException
异常
Object
element();
//
将指定元素加入队列尾部(当使用有容量限制的队列时此方法比
add(Object e)
更好)
boolean
offer(
Object
e);
//
获取队列头部元素,但是不删除该元素,如果队列为空返回
null
Object
peek();
//
获取队列头部元素并删除该元素,如果队列为空则返回
null
Object
poll();
//
获取队列头部元素并删除该元素,如果队列为空抛出
NoSuchElementException
异常
Object
remove();
LinkedList 是一个比较奇怪的类,其即实现了 List 接口又实现了 Deque 接口(Deque 是 Queue 的子接口),而 LinkedList 的实现是基于双向链表结构的,其容量没有限制,是非并发安全的队列,所以不仅可以当成列表使用,还可以当做双向队列使用,同时也可以当成栈使用(因为还实现了 pop 和 push 方法)。此外 LinkedList 的元素可以为 null 值。
PriorityQueue 是一个优先级列表队列,因为 PriorityQueue 保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序,所以当调用 peek 或者 pull 方法从队列头取元素时并不是取出最先进入队列的元素,而是取出队列中最小的元素(默认顺序),所以说 PriorityQueue 实质违反了 FIFO 队列的基本原则,从而成了优先级列表实现,同时 PriorityQueue 的实现是基于动态扩容数组的二叉树堆结构,其最大容量长度为 Int 大小,是非并发安全的队列。此外 PriorityQueue 的元素不可为 null 值。
PriorityQueue 确定最高优先级元素使用的是堆数据结构,因为堆是一棵完全树,堆中某个节点的值总是不大于或不小于其父节点值的,常见的堆有二叉堆、斐波那契堆等,二叉堆是完全二叉树或者是近似完全二叉树的一种特殊堆,其分为最大堆和最小堆,最大堆中父结点值总是大于或等于任何一个子节点值,最小堆中父结点值总是小于或等于任何一个子节点值,由于二叉堆一直是自左向右自上向下一层层填充的,所以其可以用数组来表示而不是用链表,PriorityQueue 就是采用了基于动态数组的二叉堆来确定优先级。
ArrayDeque 和 LinkedList 都实现了 Deque 接口,如果只需要 Deque 接口且从两端进行操作则一般来说 ArrayDeque 效率更高一些,如果同时需要根据索引位置进行操作或经常需要在中间进行插入和删除操作则应该优先选 LinkedList 效率高些,因为 ArrayDeque 实现是循环数组结构(即一个动态扩容数组,默认容量 16,然后通过一个 head 和 tail 索引记录首尾相连),而 LinkedList 是基于双向链表结构的。
PriorityQueue 是一个优先级队列,保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。
WeakHashMap 的工作与正常的 HashMap 类似,但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时,key/value 将会被回收。
在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段
privatestatic
final
int
DEFAULT_CAPACITY =
10;
//from HashMap.java JDK 7
static
final
int
DEFAULT_INITIAL_CAPACITY =
1<<
4;
// aka 16
Comparable 对实现它的每个类的对象进行整体排序,这个接口需要类本身去实现,若一个类实现了 Comparable 接口,实现 Comparable 接口的类的对象的 List 列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序,此外实现 Comparable 接口的类的对象可以用作有序映射(如TreeMap)中的键或有序集合(如TreeSet)中的元素而不需要指定比较器,实现 Comparable 接口必须修改自身的类(即在自身类中实现接口中相应的方法),如果我们使用的类无法修改(如 SDK 中一个没有实现 Comparable 的类),我们又想排序,就得用到 Comparator 这个接口了(策略模式)。所以如果你正在编写一个值类,它具有非常明显的内在排序关系,比如按字母顺序、按数值顺序或者按年代顺序,那你就应该坚决考虑实现 Comparable 这个接口, 若一个类实现了 Comparable 接口就意味着该类支持排序,而 Comparator 是比较器,我们若需要控制某个类的次序,可以建立一个该类的比较器来进行排序。 Comparable 比较固定,和一个具体类相绑定,而 Comparator 比较灵活,可以被用于各个需要比较功能的类使用,所以尽量推荐使用 Comparator 而不是 Comparable,因为这样可以保证单一职责原则。
Comparable 接口用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。
HashMap Hashtable Properties LinkedHashMap IdentityHashMap
TreeMap WeakHashMap ConcurrentHashMap
你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序。
keySet() values() entrySet()
Map.Entry : getKey() getValue()
1、通用的就是循环添加
2、addall()或者add等接口直接把一个list添加到另一个list,比如java就有addall方法
你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法来打印数组。由于数组没有实现 toString() 方法,所以如果将数组传递给 System.out.println() 方法,将无法打印出数组的内容,但是 Arrays.toString() 可以打印每个元素。
该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法。这有一段示例代码,是使用正确的方式来实现在遍历的过程中移除元素,而不会出现 ConcurrentModificationException 异常的示例代码。
1.类型安全2.消除强制类型转换3.减少装箱拆箱,提高性能,减少出错。在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码重用率。
泛型类型参数不能用在Java异常处理catch语句中。泛型类型擦除,Jvm无法区别异常。
需要泛型之间的类型转换怎么做?通配符?与PECS
上传文件中文乱码2.设置解析器缓冲区的大小,以及临时文件的删除3.限制上传文件类型及大小
java.text.SimpleDateFormat
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String ok = sdf.format(System.currentTimeMillis());
System.out.println(ok);
java.util.Date
Date now = new Date();
System.out.println(now);
System.out.println(now.getYear()+1900);
System.out.println(now.getMonth() + 1);
System.out.println(now.getDate());
System.out.println(now.getHours());
System.out.println(now.getMinutes());
System.out.println(now.getSeconds());
java.util.Calendar
Calendar now = Calendar.getInstance();
System.out.println(now.get(1)); System.out.println(now.get(2)+1);
System.out.println(now.get(5)); System.out.println(now.get(11));
System.out.println(now.get(12)); System.out.println(now.get(13));
DATE只保存日期,不保存时分秒
DATETIME类型用在你需要同时包含日期和时间信息的值时。MySQL检索并且以'YYYY-MM-DD HH:MM:SS'格式显示DATETIME值,支持的范围是'1000-01-01 00:00:00'到'9999-12-31 23:59:59'。
TIMESTAMP列类型提供一种类型,你可以使用它自动地用当前的日期和时间标记INSERT或UPDATE的操作。如果你有多个TIMESTAMP列,只有第一个自动更新。
DateFormat 的所有实现,包括 SimpleDateFormat 都不是线程安全的,因此你不应该在多线程序中使用,除非是在对外线程安全的环境中使用,如 将 SimpleDateFormat 限制在 ThreadLocal 中。如果你不这么做,在解析或者格式化日期的时候,可能会获取到一个不正确的结果。因此,从日期、时间处理的所有实践来说,我强力推荐 joda-time 库。
Java 中,可以使用 SimpleDateFormat 类或者 joda-time 库来格式日期。DateFormat 类允许你使用多种流行的格式来格式化日期。参见答案中的示例代码,代码中演示了将日期格式化成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy。
138、POI是做什么用的,请具体写其中涉及到的一个类?
解析Excel, Workbook-->XSSFWorkbook HSSFWorkbook
139、JSONObject是哪个包下的 net.sf.json.JSONObject
144、如果我同时发送多个ajax,多个异步或是多个同步,会出现什么问题。
发送多个同步请求效率会慢,必须等待上一个结束才能执行下一个
发送多个同步请求则可能会导致返回的结果混乱
140、请写一个校验数据格式为 10-500 的正则表达式 ([1-9]\d)|([1-4]\d{2})|(500)
141、原生ajax请求的五个步骤
第一步,创建XMLHttpRequest,第二步,注册回调函数 第三步,配置请求信息
第四步,发送数据,与服务器开始交互
第五步,创建回调函数, 接受服务器返回数据。
142、AJAX是什么? 描述ajax的原理
AJAX = Asynchronous JavaScript and XML(异步 JavaScript 和 XML),又叫异步刷新
Ajax 的原理简单来说通过 XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后用 Javascript 来操作 DOM 而更新页面。这其中最关键的一步就是从服务器获得请求数据。
XmlHttpRequest 是 ajax 的核心机制,它是在 IE5 中首先引入的,是一种支持异步请求的技术。简单的说,也就是 Javascript 可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。
143、ajax怎么实现跨域访问并且是安全的?
http:// www . google : 8080 / script/jquery.js
http:// (协议号)www (子域名) google (主域名)
8080 (端口号) script/jquery.js (请求的地址)
* 当协议、子域名、主域名、端口号中任意一各不相同时,都算不同的“域”。
* 不同的域之间相互请求资源,就叫“跨域”。
处理跨域的方法:1 -- 代理。2 -- JSONP。JSONP只支持 “GET” 请求,但不支持 “POST” 请求。3 -- XHR2(推荐方法).“XHR2”全称“XMLHttpRequest Level2”是HTML5提供的方法,对跨域访问提供了很好的支持.
145、怎么样在一个方法里面这两个ajax,其中第二个ajax,需要用到第一个的传值,可不可以两个挨着写
不能,即便挨着写也是两个不同的异步请求,很可能第二个ajax在发出请求的时候第一个ajax的值还没有传回来 同步两个请求
146、同步ajax跟异步ajax有什么不同,那个好用?
request.open(method,url,async,username,password);
async:true为异步
false为同步
异步方式客户端不需要等待服务器的响应。同步方式客户端必须等待服务器响应之后才可以继续执行后续操作。要根据实际情况选择方式,异步效率高但是对服务器造成更高的负载,当某些页面可能会发出多个请求,甚至是有组织的有计划的有队形的高强度的request时,后一个是会覆盖前一个的,则应该使用同步.
147、什么是反射?请用反射动态创建一个类的对象(写关键代码,其它可省略)
在运行状态中对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象都能调用他的任意一个方法和属性。这种动态获取信息以及调用对象方法的功能叫Java语言反射机制。JAVA语言编译之后会生成一个.class文件,反射就是通过字节码文件找到某一个类、类中的方法以及属性等。
反射,程序运行时动态获取类型信息,完成对象创建,方法调用等。例如:
Class myclass=Class.forNama("包名.类名");
Student stu=Factory.createInstance("stu1");
148、class.forname的作用
Class.forName(xxx.xx.xx)返回的是一个类。.newInstance()后才创建一个对象。
Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。
149、class.forname应用情景
情景一:加载数据库驱动的时候 情景二:使用AIDL与电话管理Servic进行通信
150、在初始化一个类,生成一个实例的时候,newInstance()和new关键字区别?
new: 强类型。相对高效。能调用任何public构造。
151、Class类的常用方法
1、getName()
一个Class对象描述了一个特定类的属性,Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
2、newInstance()
Class还有一个有用的方法可以为类创建一个实例,这个方法叫做newInstance()。例如:
x.getClass.newInstance(),创建了一个同x一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。
3、getClassLoader() 返回该类的类加载器。
4、getComponentType() 返回表示数组组件类型的 Class。
5、getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
6、isArray() 判定此 Class 对象是否表示一个数组类。
152、任务调度?
任务调度:是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务。
四种任务调度的 Java 实现:
Timer, ScheduledExecutor,开源工具包 Quartz,开源工具包 JCronTab
152、说一说你在项目中是如何使用任务调度的?
使用spring内置的quartz组件实现任务调度
quartz调度任务的流程 :
1. 提供任务对象 2. ioc容器中配置任务对象
3. 描述任务细节 JobDetail
4. 为任务设置触发器 Trigger 设置触发时间(通过cron表达式)
5. 配置总调度类 Scheduler
153、项目中数据库连接池怎么配置的,说说各个参数的含义。
基本配置:是指连接池进行数据库连接的
四个基本必需配置:传递给JDBC驱动用于连接数据库用户名、密码、URL以及驱动类名。
关键配置:
最小连接数:是数据库一直保持的数据库连接数。
初始化连接数:连接池启动时创建的初始化数据库连接数量。
最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求被加入到等待队列中。
最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间,超过时间则抛出异常,可设置参数为0或者负数使得无限等待(根据不同连接池配置)。
这两个是完全不同的概念,不存在区别,http是一种网络协议,而webservice是一种两个应用程序之间实现通信的解决方案。
但是两者之间是有联系的Webservice实现消息的传送就是将消息依附在http上进行传输的。
事务的开启方式依赖于工程中使用的事务形式
编程式事务(JDBC事务) jdbc事务是绑定在connection中的 connection.setAutoCommit(true/false)
声明式事务(spring aop事务) ioc容器中配置事务通知bean 以切入点的方式在需要开启事务的方法中引用该通知
配置webservice
302是代表暂时性转移,也就是在服务器端发生的跳转,和forward有点类似,但它们是俩种东西,只是原理类似,这种方式较容易发生一个问题,容易发生url劫持。
系统过载 - 处理方式 : 过载保护 - 使用线程池
A 当一个请求过来,线程池开启一个线程来处理,直到线程池中所有的线程都在处理请求
B 当线程池中没有空闲线程了,就将请求添加到有界队列当中,直到队列满为止
C 当队列满以后,在开启线程来处理新的请求,直到开启的线程数达到某个临界值(这个临界值可以根据当前系统的承载能力来定)
D 当开启的线程数达到临界后,任务队列又已经满了后,此时再过来的请求将被拒绝,被拒绝的请求在本地系列化,将保存的数据同步到离线数据系统进行处理
1、JBPM 2、Activiti 3、Shark 4、osworkflow
使用SmartUpload
//使用SmartUpload插件进行文件的上传
//1:引入SmartUpload
SmartUpload su = new SmartUpload();
//2:设置允许上传文件的后缀名
su.setAllowedFilesList("jpg,jpeg,gif,bmp");
//3:设置允许上传文件的总大小 这里设置的是6m
su.setTotalMaxFileSize(6*1024*1024);
//4:初始化
su.initialize(this.getServletConfig(),request,response);
//5:上传
su.upload();
//设置一个路径,将文件最终保存在此路径下
//可以使用绝对路径,也可以使用相对路径
//因为目前的定位就在服务器端,所以使用相对路径直接在服务器选取
//拿取所有上传的文件
Files files = su.getFiles();
//拿取唯一上传文件
File file = files.getFile(0);
//拿取上传文件的全名
String fileName = file.getFileName();
//组合一个要上传的路径
String path = "/image/"+fileName;
//另存到此路径
file.saveAs(path);
使用SmartUpload下载:
SmartUpload su = new SmartUpload();
//设置保存的地点,注意填写null由浏览器提示保存地址
su.setContentDisposition(null);
su.initialize(this.getServletConfig(), request,response);
su.downloadFile(路径);
有DOM,SAX,STAX等
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问
STAX:Streaming APIfor XML (StAX)
用到了数据存贮,信息配置两方面。在做数据交换平台时,将不能数据源的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。
a: 两种形式dtd schema,
b: 本质区别:schema本身是xml的,可以被XML解析器解析(这也是从DTD上发展schema的根本目的),
c:有DOM,SAX,STAX等
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问
SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问
STAX:Streaming API for XML (StAX)
1).浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
2).深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。
换言之,深拷贝把要复制的对象所引用的对象都复制了一遍。
黑盒测试关注程序的功能是否正确,面向实际用户;
白盒测试关注程序源代码的内部逻辑结构是否正确,面向编程人员;
灰盒测试是介于白盒测试与黑盒测试之间的一种测试。
单元测试(Unit Testing)是对软件基本组成单元进行的测试,如函数或是一个类的方法。这里的单元,就是软件设计的最小单位。
HTTP:超文本传输协议
FTP:文件传输协议
SMPT:简单邮件协议
TELNET:远程终端协议
POP3:邮件读取协议
Maven是一个项目管理工具(maven里面有很多jar包,用到时自动导入jar包)
包含了:项目对象模型,标准集合,项目生命周期,依赖管理系统和用来运行定义在生命周期阶段中插件目标的逻辑
Maven冲突原则:
1.短路优先 A-B -C-X(jar) A-D-X(jar) 第二条将优先
2.路径相同的情况 先声明先优先,先解析谁
可以在管理控制台中修改对应服务器的启动模式为开发或产品模式之一。或者修改服务的启动文件或者commenv文件,增加setPRODUCTION_MODE=true。
修改服务启动文件,增加 WLS_USER和WLS_PW项。也可以在boot.properties文件中增加加密过的用户名和密码.
保存在此Domain的config.xml文件中,它是服务器的核心配置文件。
Domain目录服务器目录applications,将应用目录放在此目录下将可以作为应用访问,如果是Web应用,应用目录需要满足Web应用目录要求,jsp文件可以直接放在应用目录中,Javabean需要放在应用目录的WEB-INF目录的classes目录中,设置服务器的缺省应用将可以实现在浏览器上无需输入应用名。
不同类型的EJB涉及的配置文件不同,都涉及到的配置文件包括ejb-jar.xml,weblogic-ejb-jar.xmlCMP实体Bean一般还需要weblogic-cmp-rdbms-jar.xml
缺省安装中使用DemoIdentity.jks和DemoTrust.jks KeyStore实现SSL,需要配置服务器使用Enable SSL,配置其端口,在产品模式下需要从CA获取私有密钥和数字证书,创建identity和trustkeystore,装载获得的密钥和数字证书。可以配置此SSL连接是单向还是双向的。
webservice是一种跨编程语言和跨操作系统的远程调用技术,遵循SOPA/WSDL规范。
Web Service是基于网络的、分布式的模块化组件,它执行特定的任务,遵守具体的技术规范,这些规范使得Web Service能与其他兼容的组件进行互操作。
JAXP(Java API forXML Parsing) 定义了在Java中使用DOM, SAX, XSLT的通用的接口。这样在你的程序中你只要使用这些通用的接口,当你需要改变具体的实现时候也不需要修改代码。
JAXM(Java API forXML Messaging) 是为SOAP通信提供访问方法和传输机制的API。
WSDL是一种XML 格式,用于将网络服务描述为一组端点,这些端点对包含面向文档信息或面向过程信息的消息进行操作。这种格式首先对操作和消息进行抽象描述,然后将其绑定到具体的网络协议和消息格式上以定义端点。相关的具体端点即组合成为抽象端点(服务)。
SOAP即简单对象访问协议(Simple Object Access Protocol),它是用于交换XML编码信息的轻量级协议。
UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为WebService提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。
CORBA 标准是公共对象请求代理结构(Common Object Request Broker Architecture),由对象管理组织 (Object Management Group,缩写为 OMG)标准化。它的组成是接口定义语言(IDL), 语言绑定(binding:也译为联编)和允许应用程序间互操作的协议。其目的为:用不同的程序设计语言书写在不同的进程中运行,为不同的操作系统开发。
1、可操作的的分布式应用程序
2、普遍性、使用HTTP和XML进行通信
3、Web Service 甚至可以穿越防火墙,真正的自由通信
4、通过 SOAP 协议实现异地调用
缺点一:单机应用程序 缺点二:局域网的同构应用程序
可以使用管理控制台,在它的Deployment中可以查看所有已发布的EJB
1、GIT是分布式的,SVN不是。
2、GIT把内容按元数据方式存储,而SVN是按文件。
3、GIT分支和SVN的分支不同。
4、GIT没有一个全局的版本号,而SVN有。
5、GIT的内容完整性要优于SVN。
EJB包括Session Bean、EntityBean、Message Driven Bean,基于JNDI、RMI、JAT等技术实现。
SessionBean在J2EE应用程序中被用来完成一些服务器端的业务操作,例如访问数据库、调用其他EJB组件。EntityBean被用来代表应用系统中用到的数据。
对于客户机,SessionBean是一种非持久性对象,它实现某些在服务器上运行的业务逻辑。
对于客户机,EntityBean是一种持久性对象,它代表一个存储在持久性存储器中的实体的对象视图,或是一个由现有企业应用程序实现的实体。
Session Bean 还可以再细分为 Stateful Session Bean 与 StatelessSession Bean ,这两种的 Session Bean都可以将系统逻辑放在method之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常来说,一个使用者会有一个相对应的 Stateful Session Bean 的实体。StatelessSession Bean 虽然也是逻辑组件,但是他却不负责记录使用者状态,也就是说当使用者呼叫Stateless Session Bean 的时候,EJBContainer 并不会找寻特定的 Stateless Session Bean 的实体来执行这个 method。换言之,很可能数个使用者在执行某个 Stateless Session Bean 的methods 时,会是同一个 Bean 的 Instance 在执行。从内存方面来看,Stateful Session Bean 与 StatelessSession Bean 比较, Stateful Session Bean 会消耗J2EE Server 较多的内存,然而 Stateful Session Bean 优势却在于他可以维持使用者的状态。
Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于JavaBean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。Enterprise Java Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理,EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。
SessionBean:StatelessSession Bean 的生命周期是由容器决定的,当客户机发出请求要建立一个Bean的实例时,EJB容器不一定要创建一个新的Bean的实例供客户机调用,而是随便找一个现有的实例提供给客户机。当客户机第一次调用一个Stateful Session Bean 时,容器必须立即在服务器中创建一个新的Bean实例,并关联到客户机上,以后此客户机调用Stateful Session Bean 的方法时容器会把调用分派到与此客户机相关联的Bean实例。
EntityBean:EntityBeans能存活相对较长的时间,并且状态是持续的。只要数据库中的数据存在,Entity beans就一直存活。而不是按照应用程序或者服务进程来说的。即使EJB容器崩溃了,Entity beans也是存活的。Entity Beans生命周期能够被容器或者 Beans自己管理。
EJB通过以下技术管理实务:对象管理组织(OMG)的对象实务服务(OTS),Sun Microsystems的TransactionService(JTS)、Java TransactionAPI(JTA),开发组(X/Open)的XA接口。
主要提供声明周期管理、代码产生、持续性管理、安全、事务管理、锁和并发行管理等服务。
以Stateful Session Bean 为例:其Cache大小决定了内存中可以同时存在的Bean实例的数量,根据MRU或NRU算法,实例在激活和去激活状态之间迁移,激活机制是当客户端调用某个EJB实例业务方法时,如果对应EJBObject发现自己没有绑定对应的Bean实例则从其去激活Bean存储中(通过序列化机制存储实例)回复(激活)此实例。状态变迁前会调用对应的ejbActive和ejbPassivate方法。
会话(Session)Bean ,实体(Entity)Bean消息驱动的(Message Driven)Bean
会话Bean又可分为有状态(Stateful)和无状态(Stateless)两种
实体Bean可分为Bean管理的持续性(BMP)和容器管理的持续性(CMP)两种
设置JNDI服务工厂以及JNDI服务地址系统属性,查找Home接口,从Home接口调用Create方法创建Remote接口,通过Remote接口调用其业务方法。
EJB容器:Enterprisejava bean 容器。更具有行业领域特色。他提供给运行在其中的组件EJB各种管理功能。只要满足J2EE规范的EJB放入该容器,马上就会被容器进行高效率的管理。并且可以通过现成的接口来获得系统级别的服务。例如邮件服务、事务管理。
JNDI:(JavaNaming & Directory Interface)JAVA命名目录服务。主要提供的功能是:提供一个目录系统,让其它各地的应用程序在其上面留下自己的索引,从而满足快速查找和定位分布式应用程序的功能。
JMS:(JavaMessage Service)JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。
JTA:(JavaTransaction API)JAVA事务服务。提供各种分布式事务服务。应用程序只需调用其提供的接口即可。
JAF:(JavaAction FrameWork)JAVA安全认证框架。提供一些安全控制方面的框架。让开发者通过各种部署和自定义实现自己的个性安全控制策略。
RMI/IIOP:(RemoteMethod Invocation /internet对象请求中介协议)他们主要用于通过远程调用服务。例如,远程有一台计算机上运行一个程序,它提供股票分析服务,我们可以在本地计算机上实现对其直接调用。当然这是要通过一定的规范才能在异构的系统之间进行通信。RMI是JAVA特有的。
1.单一职责原则——SRP
让每个类只专心处理自己的方法。
2.开闭原则——OCP
软件中的对象(类,模块,函数等)应该对于扩展是开放的,但是对于修改是关闭的。
3.里式替换原则——LSP
子类可以去扩展父类,但是不能改变父类原有的功能。
4.依赖倒置原则——DIP
应该通过调用接口或抽象类(比较高层),而不是调用实现类(细节)。
5.接口隔离原则——ISP
把接口分成满足依赖关系的最小接口,实现类中不能有不需要的方法。
6.迪米特原则——LOD
高内聚,低耦合。
首先要明白这道题目的考查点是什么,一是大家首先要对计算机原理的底层细节要清楚、要知道加减法的位运算原理和知道计算机中的算术运算会发生越界的情况,二是要具备一定的面向对象的设计思想。
首先,计算机中用固定数量的几个字节来存储的数值,所以计算机中能够表示的数值是有一定的范围的,为了便于讲解和理解,我们先以byte 类型的整数为例,它用1个字节进行存储,表示的最大数值范围为-128到+127。-1在内存中对应的二进制数据为11111111,如果两个-1相加,不考虑Java运算时的类型提升,运算后会产生进位,二进制结果为1,11111110,由于进位后超过了byte类型的存储空间,所以进位部分被舍弃,即最终的结果为11111110,也就是-2,这正好利用溢位的方式实现了负数的运算。-128在内存中对应的二进制数据为10000000,如果两个-128相加,不考虑Java运算时的类型提升,运算后会产生进位,二进制结果为1,00000000,由于进位后超过了byte类型的存储空间,所以进位部分被舍弃,即最终的结果为00000000,也就是0,这样的结果显然不是我们期望的,这说明计算机中的算术运算是会发生越界情况的,两个数值的运算结果不能超过计算机中的该类型的数值范围。由于Java中涉及表达式运算时的类型自动提升,我们无法用byte类型来做演示这种问题和现象的实验,大家可以用下面一个使用整数做实验的例子程序体验一下:
1 inta = Integer.MAX_VALUE;
3 intb = Integer.MAX_VALUE;
5 intsum = a + b;
7 System.out.println(“a=”+a+”,b=”+b+”,sum=”+sum);
先不考虑long类型,由于int的正数范围为2的31次方,表示的最大数值约等于2*1000*1000*1000,也就是20亿的大小,所以,要实现一个一百亿的计算器,我们得自己设计一个类可以用于表示很大的整数,并且提供了与另外一个整数进行加减乘除的功能,大概功能如下:
(1)这个类内部有两个成员变量,一个表示符号,另一个用字节数组表示数值的二进制数
(2)有一个构造方法,把一个包含有多位数值的字符串转换到内部的符号和字节数组中
(3)提供加减乘除的功能
1 public class BigInteger {
2 int sign;
3 byte[] val;
5 public Biginteger(String val) {
6 sign= ;
7 val= ;
8 }
10 public BigInteger add(BigInteger other) {
11 }
13 public BigInteger subtract(BigInteger other) {
14 }
16 public BigInteger multiply(BigInteger other) {
17 }
19 public BigInteger divide(BigInteger other) {
20 }
22 }
Set:集合,元素是无序的(因为没有索引),元素不可以重复。可以有null元素。
|--->HashSet(JDK1.2):底层数据结构是哈希表、存取速度快、元素唯一、线程不同步。
保证性元素唯一的原理:
先判断元素的hashCode值是否相同,再判断两元素的equals方法是否为true
(往HashSet里面存的自定义元素要复写hashCode和equals方法,以保证元素的唯一性!)
|--->TreeSet:底层数据结构式二叉树。可以对Set集合中的元素进行排序。元素有序、线程不同步。
保证元素唯一性的依据:compareTo方法return 0
TreeSet排序的第一种方式:让元素自身具备比较性,比如八种基本数据类型或则字符串,
实现Compareble接口,覆盖compareTo方法,
此方式是元素的自然顺序
TreeSet排序的第一种方式:当元素自身不具备比较性(比如存储学生对象时)或者具备的
比较性不是我们所需要的比较性时(比如想字符串的长度排序),
此时就需要让集合自身具备自定义的比较性。
那如何让集合自身具备比较性呢?可在集合初始化时,
就让集合具备比较方式。即定义一个类,
实现Comparator接口,覆盖compare方法。
HashSet是如何保证元素唯一性的呢?
**如果两元素的hashCode值不同,则不会调用equals方法
**如果两元素的hashCode值相同,则继续判断equals是否返回true;
**hashCode和equals方法虽然定义在自定义对象类里面,但不是我们手动调用
而是往HashSet集合里面存储元素的时候,集合底层自己调用hashCode和equals
它自己拿对象去判断,自己判断两元素是否是同一个元素。
(2)TreeSet:
TreeSet要求往里面存的元素具备比较性,否则会报错。
TreeSet排序的第一种方式:让元素自身具备比较性
定义对象类,实现Compareble接口,复写compareTo方法,此方式是元素的自然顺序
reeSet排序的第一种方式:让集合具备比较性
当元素自身不具备比较性(比如存储学生对象时)或者具备的
比较性不是我们所需要的比较性时(比如想字符串的长度排序),
此时就需要让集合自身具备自定义的比较性。
那如何让集合自身具备比较性呢?可在集合初始化时,
就让集合具备比较方式。即定义一个类,
实现Comparator接口,覆盖compare方法。
基本数据类型或字符串对象均实现了Comparable接口,故同种类型基本数据间具备比较性,即自然顺序。
**Map:顶层接口,该集合存储的是键值对,而且键是唯一的,Map和Set很像,Set集合底层就是使用了Map集合。
Map集合没有迭代器,要取出元素必须先将Map集合转换成Set集合才能遍历元素
|--->HashTable(JDK1.0):
底层是哈希表数据结构;
不可以使用null键和null值;
用作键的对象必须实现hashCode和equals方法来保证键的唯一性
线程同步,效率低
|--->HashMap(JDK1.2):
底层是哈希表数据结构;
允许使用null键和null值;
线程不同步,效率高;
保证元素唯一性的:
原理:先判断元素的hashCode值是否相同,再判断两元素的equals方法是否为true
(往HashSet里面存的自定义元素要复写hashCode和equals方法,
以保证元素的唯一性!)
Map集合没有迭代器,以下是Map的两种取出方式:
第一种:Set
返回此映射中包含的键的Set视图,将Map集合中所有的键存入Set集合,然后再通过Set集合的
迭代器取出所有的键,再根据get方法获取每个键的值;
第二种:Set
返回此映射中包含的映射关系的Set视图,将Map集合中的映射关系存入到Set集合中,
这个映射关系的数据类型是Map.entry,再通过Map.Entry类的方法再要取出关系里面的键和值
Map集合和Collection集合的区别?
1,
Map中一次存储是键值对。
Collection中一次存储是单个元素。
2,
Map的存储使用的put方法。
Collection存储使用的是add方法。
3,
Map集合没有迭代器,Map的取出,是将Map转成Set,在使用迭代器取出。
Collection取出,使用就是迭代器。
4,
如果对象很多,必须使用集合存储。
如果元素存在着映射关系,可以优先考虑使用Map存储或者用数组,
如果没有映射关系,可以使用Collection存储。
堆栈和队列
堆栈:先进后出,比如杯子里的水
队列:先进先出,比如水管的水