Java
中重要类
一、Object类
类Object是类层次结构的根类。每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法。Object类的方法
1、clone()克隆,拷贝
一个对象参与序列化过程,那么对象流会记录该对象的状态,当再次序列化时,会重复序列化前面记录的对象初始状态,我们可以用对象克隆技术来解决这个问题(注意:直接用等号赋值只是把对象的地址复制一份,而用clone方法是把对象复制一份)
//Object类中提供的clone方法是protected修饰的,故我们要覆盖改方法publicclassStudentimplementsjava.lang.Cloneable{privateStringname;privateintage;
publicStudent(Stringname,intage){this.name=name;this.age=age;}
//set和get方法
publicvoidclone()throwsCloneNotSupportedException{super.clone();}}
publicclassTest{
publicstaticvoidmain(String[]args)throwsCloneNotsupportedException{
Students1=newStudent(\"zhanglimeng\//直接用等号赋值,会发现指向的是同一个对象,所以s2的age变了之后,s1的age也发生了改变。Students2=s1;s2.setAge(20);Students3=null;
System.out.println(s1.getAge());
//注意在这里是不能直接调用object的clone方法的
//s3=s1.clone();会报错,因为clone是protected修饰的//在Student类中添加了clone方法时候可以调用了s3=(Student)s1.clone();
//需要让Student类实现java.lang.Cloneable接口,要注意的是cloneable接口是标记接口,它只是标记该类对象可以被克隆,标记接口中没有方法。否则会抛出CloneNotSupportedException异常s3.setAge(40);
//注意观察下一句s1的age有没有发生变化System.out.println(s1.getAge());
wangzg@tarena.com.cn
}}
什么是浅克隆?什么是深克隆?
1)前者是把对象的内存空间中的数据直接复制一份,不管其中的数据类型是引用类型还是基本类型,这样会导致两个对象可能有指向同一个对象的属性。这样新对象和原对象还是有关系的。
2)后者是:在复制对象的时候,不是把所有属性的值原封不动的拷贝过来,而是在拷贝的时候分类,如果要拷贝的属性是引用,就会把引用指向的对象重新创建一份,然后用一个新的引用指向这个新对象,把这个新对象的引用放在复制的对象中;如果要拷贝的属性是基本类型,直接复制就可以了。3)疑问:为什么String类型的属性在浅克隆的时候也是互不影响的?关键是String这个类型决定的,一旦声明并且复制了一个String类型的对象之后,那么这个对象的值永远都不会改变,比如Strings=\"张三\";如果你重新给s赋了值之后,比如s=\"lisi\指向的对象的值改成了内容lisi,而是重新创建了一个内容为lisi的对象,把这个对象的地址给了引用。2、equals(Objectobj)
用来判断对象的值是否相等。前提是覆盖了equals方法。Object类中的equals方法判断的依然是地址注意:String类已经覆盖了equals方法,所以能用equals来判断String对象的值是否相等。
下面是覆盖equals方法的标准流程:publicbooleanequals(Objectobj){//第一步:现判断两个对象地址是否相等if(this==obj)returntrue;
//第二步:如果参数是null的话直接返回false;if(obj==null)returnfalse;
//第三步:如果两个对象不是同一个类型直接返回falseif(objinstanceofStudent){Students=(Student)obj;
if(s.name.equals(this.name)&&s.age==this.age){//比较规则由我们自己制定
returntrue;}}
returnfalse;}
覆盖equals的原则:
自反性(自己=自己)、2.对称性(y=x则x=y)、
3.一致性(多次调用,结果一致)、4.传递性(A=B,B=C则A=C)。
非空原则:t1.equals(Null)返回False;(如果t1不等于空)
wangzg@tarena.com.cn
注意:
==和equals的区别
1.基本类型用==去比较比的是值是否相等.
2.如果是对象用==比是两个引用中的地址值是否相等3.equals比较两个对象内容是否相等3、finalize()
当java的垃圾回收器回收对象的时候,会自动调用这个方法,所以这个方法中一般写资源回收的代码,所以这个方法和构造方法是一对。是由垃圾回收器自动调用的,不是程序员自己调用的。垃圾回收器是否启动不是程序员说了算。
也是需要在子类中去覆盖,因为每个类释放什么资源都不一样。4、toString()
给出这个对象的字符串的表示方式
当一个对象被打印的时候,其实会去调用这个对象的toString方法,即打印的是toString方法的返回值。
Object类的toString方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于:
getClass().getName()+'@'+Integer.toHexString(hashCode())5、hashCode()
返回该对象的哈希码值。hashCode的常规协定是:在Java应用程序执行期间,在对同一对象多次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据equals(Object)方法,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。
如果根据equals方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。实际上,由Object类定义的hashCode方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是JavaTM编程语言不需要这种实现技巧。)
总结:在Object中介绍的clone、equals、finalize、toString方法都是需要程序员自己去覆盖的,注意什么时候去覆盖。二、String类
概念(必须掌握的)
(1)String:是一组不可改变的unicode的字符序列。
理解不变——不管对String对象作任何操作,原来的String是不可改变的。一个String的值在一次虚拟机里是不会改变的。
Stringname=“zhanglimeng”这里的String值“zhanglimeng”是共享的,它的值在String常量池里。池:堆里的一片独立空间,用一个存储区域来存放一些公用资源以减少存储空间
wangzg@tarena.com.cn
的开销。目的是拿空间换时间,让运算效率更高。
Stringname1=newString(“zhanglimeng”)这里的值是独立的,它的值在堆里分配空间。
所以name==name1的结果是false;
name.equals(name1)的结果是true;//因为String的实现中重写了equals()方法。
Strings1=newString(\"abc\");Strings2=newString(\"abc\");Strings3=\"abc\";Strings4=\"abc\";Strings5=s3+\"d\";Strings6=s4+\"d\";Strings7=s1+\"d\";Strings8=s2+\"d\";Strings9=\"abcd\";Strings10=\"abc\"+\"d\";结果:
System.out.println(s1==s2);f
System.out.println(s1.equals(s2));tSystem.out.println(s3==s4);t
System.out.println(s1.equals(s4));tSystem.out.println(s5==s6);fSystem.out.println(s7==s8);fSystem.out.println(s9==s10);tString里的常用方法(重点掌握)(1)publiccharcharAt(intindex)
返回指定索引处的char值。索引范围为从0到length()-1。
序列的第一个char值在索引0处,第二个在索引1处,依此类推,这类似于数组索引。
(2)indexOf(Stringstr)
返回指定字符在此字符串中第一次出现处的索引,如果找不到就返回-1。例如:
\"abcd\".indexOf(\"a\")结果是0;\"abcd\".indexOf(\"e\")结果是-1;(3)matches(Stringregex)
判断此字符串是否匹配给定的正则表达式正则表达式:预定义字符.代表任意字符\\d数字[0-9]\\D非数字[^0-9]
\\s空白字符[\\\n\\f\\r]\\S非空白字符[^\\s]
wangzg@tarena.com.cn
\\w单词字符[a-zA-Z_0-9]\\W非单词字符[^\\w]^行的开头$行的结尾
\\表示转义字符。
[]表示在其中任选择一个。
?表示出现0~1次可用于选择((.cn)|(.com))?//|代表或的关系
+表示出现1~n次比较常用例:[0-9]+*表示出现0~n次例如:[0-9]*{n}表示必须出现n个{n,}表示出现的个数>n
{n,m}表示n<=x<=m表示n到m之间。
()括号里面的作为一个整体,提升优先级。常用正则:
^[\\w-]+[@][\\w_-]+(\\com|\\.net)$email地址^1[358]\\d{9}$国内手机
^(\\d{3}-|\\d{4}-)?(\\d{8}|\\d{7})$国内固定电话^[1-9]\\\\d{4,9}$腾讯QQ号^[\一-\龥]$中文字符
^\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3}$IP地址
匹配帐号是否合法(字母开头,允许6-16位,允许字母数字下划线):^[a-zA-Z][\\w]{5,15}$
^(\\d{2}|\\d{4})-((0([1-9]{1}))|(1[0-2]))-(([0-2]([1-9]{1}))|(3[0|1]))$年-月-日
\"^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$\"IP地址(绝对)
(4)split(Stringregex)
根据给定的正则表达式的匹配来拆分此字符串。
Stringmessage=\"张三,男,25;李四,女,23;王老五,男,30\";//将上面字符串的信息封装成几个Pseron对象存入数组String[]strs=message.split(\";\");Person[]p=newPerson[strs.length];for(inti=0;i 使用指定的字符集将此String解码为字节序列,并将结果存储到一个新的字节数组中。 wangzg@tarena.com.cn 常用于转换编码: 如:newString(src.getBytes(oldCharsetName),newCharsetName);(6)trim() 返回去除首尾的空白后的字符串 常用在从用户网页的输入框获得的字符串后,去除首尾的空白(7)substring() (a)Stringsubstring(intbeignIndex)子串为指定下标到字符串结尾\"abcdefg\".substring(3); (b)Stringsubstring(intbeignIndex,intendIndex)子串为指定开始下标到结束下标,不包括结束下标\"abcdefg\".substring(3,5);(8)compareTo intcompareTo(StringanthorString)按字典顺序比较两个字符串 intcompareToIgnoreCase(StringanthorString)不考虑大小写,按字典顺序比较两个字符串(9)length() 返回字符串的长度 三、StringBuffer&StringBuilder 当对String进行频繁的修改时,应该用StringBuffer或StringBuilder代替String StringBuffer是线程安全的(允许多个人同时操作),效率较低StringBuilder是线程不安全的(多个人操作时可能出错),效率高(1)Stringstr=\"1\"+\"2\"+\"3\"+\"4\";在生成str的过程中,会在串池中产生多余对象\"12\而我们真正需要的只有最后的对象\"1234\",不管在时间上还是在内存上都会造成相当大的浪费,所以应该使用StringBuffer(线程安全的)或者StringBuilder(线程不安全的)。解决方案:Strings=\"\"; StringBufferbuffer=newStringBuffer(\"1\");buffer.append(\"2\");buffer.append(\"3\");buffer.append(\"4\");s=buffer.toString(); (2)StringBuffer和StringBuilder没有重写equals()方法;例子: StringBuffersb1=newStringBuffer(\"abc\");StringBuffersb2=newStringBuffer(\"abc\");System.out.println(sb1==sb2); System.out.println(sb2.equals(sb2));//equals没有重写。System.out.println(\"\"); System.out.println(sb1.capacity()+\":\"+sb1.length());//19:3结果都是false。 wangzg@tarena.com.cn (3)reverse()方法可以使字符串反转(4)append(?) 将指定参数的字符串追加到此字符序列(5)capacity()返回当前容量 (6)length()返回长度(字符数)四、Math类 Math类是final类型的,因此不能有子类; Math类的构造是private类型的,因此不能实例化;提供了诸多用于数学计算的静态方法:返回类型方法名及描述参数类型 Int/long/float/doubleabs()返回绝对值int/long/float/doubleInt/longfloat/doublemax()返回两者较大者int/long/float/oubleInt/long/float/doublemin()返回两者较小者int/long/float/doubledoublepow(x,y)返回x的y词幂doubledoublerandom()返回[0-1)之间随机数long/intround()四舍五入double/float五、BigDecimal类 如果实际应用程序允许存在适当误差,那么可以使用float或double类型;如果需要进行精确运算,则应该使用java.math.BigDecimal类构造方法 BigDecimal(doubleval) 将double转换为BigDecimal,后者是double的二进制浮点值准确 的十进制表示形式。(注:并不能解决误差问题,需将double转为String构造) BigDecimal(intval) 将int转换为BigDecimal。BigDecimal(longval) 将long转换为BigDecimal。BigDecimal(Stringval) 将BigDecimal的字符串表示形式转换为BigDecimal。该类中的方法: add(BigDecimalb):进行精确的加法运算; subtract(BigDecimalb):进行精确的减法运算;multiply(BigDecimalb):进行精确的乘法运算; divide(BigDecimalb,intscale,RoundingModemore):进行除法运算; •参数scale,指定需要精确到小数点以后几位;•参数mode,指定小数部分的舍入模式 –BigDecimal.ROUND_HALF_UP,表示四舍五入;doubleValue()将此BigDecimal转换为double。 equals(Objectx)比较此BigDecimal与指定的Object的相等性floatValue()将此BigDecimal转换为float。intValue()将此BigDecimal转换为int。 wangzg@tarena.com.cn longValue()将此BigDecimal转换为long。六、日期类 java.util.Date:包装了一个long类型的数据; 表示GMT(格林尼治标准时间)的1970年1月1日00:00:00到当前所相差的毫秒数; 构造:Date()返回当前系统时间 方法:longgetTime()返回自1970年1月1日00:00:00GMT以来此Date对象表示的毫秒数。 其余构造与方法均已过时不推荐使用 java.util.Calendar抽象类其子类GregorianCalendar属性 HOUR_OF_DAY指示一天中的小时。DAY_OF_MONTH指示一个月中的某天。MONTH指示月份。YEAR指示年字。方法 IntcompareTo(CalendaranotherCalendar) 比较两个Calendar对象表示的时间值(从历元至现在的毫秒偏移量)。CalendargetInstance() 使用默认时区和语言环境获得一个日历。DategetTime() 返回一个表示此Calendar时间值(从历元至现在的毫秒偏移量)的Date对象。setTime(Datedate) 使用给定的Date设置此Calendar的时间。LonggetTimeInMillis() 返回此Calendar的时间值,以毫秒为单位。roll(intfield,booleanup) 在给定的时间字段上添加或减去(上/下)单个时间单元,不更改更大的字段。roll(intfield,intamount) 向指定日历字段添加指定(有符号的)时间量,不更改更大的字段java.text.DateFormat DateFormat是日期/时间格式化子类的抽象类 SimpleDateFormat是一个以与语言环境有关的方式来格式化和解析日期的具体类。 SimpleDateFormat(Stringpattern)用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。yyyy-MM-ddHH:mm:ss年-月-日时:分:秒 Stringformat(Datedate)将一个Date格式化为日期/时间字符串。 Dateparse(Stringsource)从给定字符串的开始解析文本,以生成一个日期。七、类型封装类 java为每一个简单数据类型提供了一个封装类,使每个简单数据类型可以被Object来装载。除了int(Integer)和char(Character),其余类型首字 wangzg@tarena.com.cn 母大写即成封装类类型名。 封装类、字符串、基本类型互相转换 int----------------------newInteger(x)---------------->IntegerString-----------------Integer.valueOf(s)---------------->IntegerInteger-----------------x.toString()--------------------->Stringint----------------------100+””------------------------->StringString------------------Integer.parseInt(s)--------------->intInteger-----------------Integer.intValue()--------------->int使用封装类的好处: 1、能把String转成封装类对应的基本类型 2、为基本类型记录一些信息,如:最大值,最小值3、实现了基本类型的对象化处理。自动封箱AutoBoxing/自动解封自动封箱和自动拆箱,它实现了简单类型和封装类型的相互转化时,实现了自动转化。 byteb-128~127 Byteb在以上数量的基础上多一个null简单类型和封装类型之间的差别 封装类可以等于null,避免数字得0时的二义性。Integeri=null; intii=i;//会抛出NullException异常。相当于intii=i.intValue();Integeri=1;//相当于Integeri=newInteger(1);i++;//i=newInteger(i.intValue()+1);在基本数据类型和封装类之间的自动转换5.0之前 Integeri=newInteger(4);intii=i.intValue();注:抽象类Number是BigDecimal、BigInteger、Byte、Double、Float、Integer、Long和Short类的父类。 5.0之后 Integeri=4; publicvoidm(inti){......}publicvoidm(Integeri){......}以上两个函数也叫方法重载 自动封箱解箱只在必要的时候才进行。能不封箱找到匹配的就不封箱。2、静态导入StaticImport 使用类中静态方法时不用写类名 System.out.println(Math.round(PI));可以用以下代码实现: importstaticjava.lang.System.*;//注意,要写\"类名.*\"importstaticjava.lang.Math.*;out.println(round(PI)); wangzg@tarena.com.cn 注意:静态引入的方法不能重名3、for-each 统一了遍历数组和遍历集合的方式 for(Objecto:list){//Objecto表示每个元素的类型,list表示要遍历的数组或集合的名字 System.out.println(o);//打印集合或数组中的每个元素} 4、可变长参数 处理方法重载中,参数类型相同,个数不同的情况publicvoidm(int...is){.....}int...is相当于一个int[]is 编译器会把给定的参数封装到一个数组中,再传给方法 在一个方法中只能有一个可变长参数,而且,必须放在最后一个参数的位置内部类 内部类也就是定义在类内部的类。是编译时语法。内部类的分类: 成员内部类MenberInnerClass静态内部类、StaticInnerClass局部内部类、LocalInnerClass 匿名内部类AnonymousInnerClass(重点必须掌握) (注意:前三种内部类与变量类似,所以可以对照参考变量)1、成员内部类 四个访问权限修饰符都可以修饰成员内部类。 内部类和外部类在编译时是不同的两个类,内部类对外部类没有任何依赖。内部类是一种编译时语法,在编译时生成的各自的字节码文件(Outer.class和Outer$Inner.class),内部类和外部类没有关系。内部类中可以访问外部类的私有成员。 作为外部类的一个成员存在,与外部类的属性、方法并列。内部类和外部类的实例变量可以共存。在内部类中访问实例变量:this.属性 在内部类访问外部类的实例变量:外部类名.this.属性。成员内部类的特点: (1)内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类声明为private,但是对于处于其内部的内部类还是可见的。) (2)用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。 注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。(3)成员内部类不能含有静态成员。建立内部类对象时应注意: 在外部类的内部可以直接使用inners=newinner();(因为外部类知道inner是 wangzg@tarena.com.cn 哪个类,所以可以生成对象。)publicclassMemberInner{privateinti=1; privatestaticinti2=0;publicclassTest{inti=2; publicvoidtest(){ System.out.println(this.i);//内部类属性i System.out.println(MemberInner.this.i);//外部类属性iSystem.out.println(i2);go();}} publicvoidgo(){ System.out.println(\"go\");} publicstaticvoidmain(String[]args){MemberInnermi=newMemberInner();Testtest=mi.newTest();test.test();}} 而在外部类的外部,要生成(new)一个内部类对象,需要首先建立一个外部类对象(外部类可用),然后在生成一个内部类对象。内部类的类名是外部类类名.内部类类名。 publicclassTestMenber{ publicstaticvoidmain(String[]args){ MemberInnerme=newMemberInner();MemberInner.Testtest=me.newTest();test.test();}} 2、静态内部类 静态内部类定义在类中,任何方法外,用staticclass定义。静态内部类只能访问外部类的静态成员。 生成(new)一个静态内部类对象不需要外部类对象:这是静态内部类和成员内部类的区别。 静态内部类的对象可以直接生成:Outer.Innerin=newOuter.Inner(); 而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。 //外部类的内部 publicclassStaticInner{ wangzg@tarena.com.cn privateinti1=0; privatestaticinti2=0;privatestaticclassStaticIn{publicvoidtest(){ //i1=1;//static内部类只能使用static变量i2=1; System.out.println(i2);}} publicstaticvoidmain(String[]args){StaticInsi=newStaticIn();si.test(); }} //外部类的外部 publicclassTestStatic{ publicstaticvoidmain(String[]args){ StaticInner.StaticInst=newStaticInner.StaticIn();st.test();}} 3、局部内部类 在方法中定义的内部类称为局部内部类。 与局部变量类似,在局部内部类前不加修饰符public和private,其范围为定义它的代码块。注意: 局部内部类不仅可以访问外部类私有实例变量,还可以访问外部类的局部常量(也就是局部变量必须为final的) 在类外不可直接访问局部内部类(保证局部内部类对外是不可见的)。在方法中才能调用其局部内部类。 通过内部类和接口达到一个强制的弱耦合,用局部内部类来实现接口,并在方法中返回接口类型,使局部内部类不可见,屏蔽实现类的可见性。publicclassLocalInner{inti1=0; staticinti2=0; publicvoidtest(inti3){ finalinti4=0;//可以使用final的局部变量classLocalIn{ publicvoidtest1(){System.out.println(i1);System.out.println(i2);//System.out.println(i3); wangzg@tarena.com.cn System.out.println(i4);}} System.out.println(i4);LocalInli=newLocalIn();li.test1();} publicstaticvoidmain(String[]args){LocalInnerli=newLocalInner();li.test(3);}} 4、匿名内部类 匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口或继承某个类,并只创建一次。匿名内部类的特点: (1)一个类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的事先或是覆盖。 (2)只是为了获得一个对象实例,不许要知道其实际类型。(3)类名没有意义,也就是不需要使用到。 注:一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。 因其为局部内部类,那么局部内部类的所有限制都对其生效。匿名内部类是唯一一种无构造方法类。大部分匿名内部类是用于接口回调用的。 匿名内部类在编译的时候由系统自动起名Out$1.class。 如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。因匿名内部类无构造方法,所以其使用范围非常的有限。匿名内部类的写法: publicinterfaceAnnFace{voidtest();} publicclassTestAnn{ publicvoidgo(AnnFaceaf){af.test();} publicstaticvoidmain(String[]args){TestAnnta=newTestAnn();ta.go(newAnnFace(){@Override publicvoidtest(){ System.out.println(\"ann\");} wangzg@tarena.com.cn });}} 注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。__ 因篇幅问题不能全部显示,请点此查看更多更全内容