PS:
在进行Java环境配置时,都会遇到Path和classpath的配置,为什么要这两个系统环境变量,以及如何配置呢?
1、Path的作用: (运行应用程序--先当前目录--后Path路径下寻找)
Path是一个十分重要的系统变量,它的值是一些路径。一般情况下,如果我们想在Dos命令行某个exe的应用程序,则首先要切换到该exe文件所在的目录,然后才能运行。但,如果我们将该exe应用程序所在的目录写入到Path环境变量中,就可以在任意目录下直接运行该exe应用程序。这就是Path的功能。即,当通过Dos命令行运行我们输入的应用程序时,系统会先在当前目录下寻找目标文件,如果找不到,就会去Path中记录的路径去寻找。
而Java的JDK中,其Bin目录下就有许多工具,这些工具都是exe文件,比如常见的javac.exe和java.exe。所以,如果我们想在系统的任何目录下都能调用这些工具,比如想在任何目录下都能编译java源文件,就需要将javac.exe所在的目录(即bin目录),添加到系统环境变量Path中。
2、classpath的作用:(寻找类文件(即:class文件)---先classpath路径下,然后当前目录)
我们知道Java虚拟机JVM负责执行Java的类文件,而JVM是如何找到需要执行的class文件呢?这就需要classpath这个环境变量了,JVM会优先去classpath中所记录的路径去按顺序查找目标class文件。如果没有设置classpath这个环境变量,则JVM只会在当前目录下进行查找。
3、设置classpath的一些注意事项:
classpath的设置中,如果设置完路径后,其末尾没有带有分号(;),则表示JVM仅仅只去指定的目录查找类文件,如果找不到,也不会在当前所在目录查找;如果带有分号作结尾,则表示会先去指定目录下查找,若找不到,则会在当前目录下重新查找;而设置中的点号(.),就代表当前目录;
一 java基本类型的转换的注意
对两个容量不一样的数据类型的变量进行算术运算时,java会自动将小容量的变量进行精度提升,然后再进行运算,得到的结果类型是提升后的大容量的数据类型.如果将该结果赋值给小容量的数据类型的变量,则必须进行强制的类型转换,否则编译程序会报损失精度错.例如:
char c='a';
int i = 5;
首先,因为char类型是16位的,而int类型是32位的,在进行
(c+i) 运算时,自动将c提升到32位,然后与i相加,得到的结果是32位的,而此时
c=c+i; 必然报错,因为如果赋值成功,只是把低16位赋给了c,这个虽然正是楼主想要的结果,但是编译程序却不能判定你的意图是什么.而这样:
c=(char)(c+i); 就没问题了.
若说字符和整数不能相加是不对的.java延续了c的习惯,只不过相加之后赋值时要进行类型转换.
至于为什么c+=i;能编译通过并得到正确结果,而 c=c+i; 却报错,是因为它们并不像楼主认为的那样是等价的.
java规范中说:
E1 op=E2 实际上等价于 : E1=(T)( (E1)op(E2) ),例如E1+=E2
其中T是E1的数据类型.这种类型转换或者是一个恒等转换,或者是一个窄化转换.
这个说明java是一种强类型的语言,对数据类型的要求是非常严格的,否则会引起混乱.
下面解释一下三种类型转换:
恒等转换(相同容量的两个数据类型之间的赋值);
拓宽转换(小容量类型赋值给大容量类型);
窄化转换(大容量赋值给小容量类型).
实际上,前两种转换都是编译程序自动执行的,而窄化转换则要求必须显式的执行.
如果改一下:i+=c; i=i+c; 则这两个语句都会编译通过,很容易理解.
1.2 ++i---VS--i++(详细见注释)
public static void main(String[] args) {
int j = 1;
System.out.println(j++);// 结果是1 ,J++操作的结果,下次有效
System.out.println(j);// 结果是2
int i = 1;
i++; // 等价于 i=i+1,但是下次有效
System.out.println(i); // 结果是2
++i; // 等价于 i=i+1,立刻有效 3
System.out.println(i); // 3
//System.out.println(++i); // 等价于 i=i+1,立刻有效 4//(i 以上一条的语句为其执行结果为其初始值,即i=3)
i = i++; // 等价于 int temp=i;i=i+1; i=temp
System.out.println(i); // 3,not 4 特别注意
i = ++i; // 等价于 i=i+1; 即等价于 ++i;
//本条语句会提示 警告 :The assignment to variable i has no effect
System.out.println(i); // 4
}
二 Java的数据类型
2.1-----8种基本类型
2.1.1-----四种整型
byte 1字节 -128——127
short 2 字节 -32,768 —— 32,767
int 4 字节 -2,147,483,648 ——2,147,483,647(超过20亿)
long 8 字节 -9,223,372,036,854,775,808——9,223,372,036854,775,807
注释:java中所有的数据类所占据的字节数量与平台无关,java也没有任何无符号类型
2.2.2------俩种浮点类型
float 4 字节 32位IEEE 754单精度(有效位数 6 – 7位)double 8 字节 64位IEEE 754双精度(有效位数15位)
2.2.3-----1种Unicode编码的字符单元char 2 字节 整个Unicode字符集2.2.4--- 1种真值类型boolean 1 位 True或者false
2.2 ----三种引用类型
类class、接口interface、 数组array
switch语句 的值可为int,char ,byte以及枚举类型,其它都不可以,如: long ,String
在每一个分支里面都必须写break,此语句表示退出整个switch()循环;
2.3 包装类型
2.3 .1 把字符串解析成基本类型的数据:对应包装类的静态方法parseXxx();
例如:int i = Integer.parseInt("1234");
Integer integer = Integer.valueOf("1234");
2.3.2 java1.5增加了自动装箱(autoboxing)和自动拆箱(auto-unboxing)
基本类型和装箱基本类型的区别:
1:基本类型只有值,而装箱类型除了它对应基本类型的所以功能值之外,还有个非功能值:null
2:基本类型通常比装箱类型更节省时间和空间
3
什么时候应该使用装箱类型:当在一项操作中混合使用基本类型和装箱类型时,装箱类型就会自动拆箱。
1:作为集合中的元素、键和值
2:在参数化类型中,必须使用装箱类型作为类型参数,因为java不允许使用基本类型
3:在进行反射的方法调用时,必须使用装箱基本类型
static
Integer i;
2
public
static
void
main(String[] args) {
3
if
(i==
42
)
4
System.out.println(
"Unbelievable"
);
5
}
PS
6
//结果java.lang.NullPointerException
7
原因: i是一个Integer,而不是一个
int
,所有初始值是
null
8
当在一项操作中混合使用基本类型和装箱类型时,装箱类型就会自动拆箱。
如果null
对象引用被自动拆箱,就会得到一个NullPointerException
当static Integer i=42时,则i==42,为true,会自动拆箱。
2.4 java中判断是否为数字的三种方法
1.用JAVA自带的函数,isDigit(char ch)
- public static boolean isNumeric(String str){
- for (int i = str.length();--i>=0;){
- if (!Character.isDigit(str.charAt(i))){
- return false;
- }
- }
- return true;
- }
2.用正则表达式
- public static boolean isNumeric(String str){
- Pattern pattern = Pattern.compile("[0-9]*");
- return pattern.matcher(str).matches();
- }
3.用ascii码
- public static boolean isNumeric(String str){
- for(int i=str.length();--i>=0;){
- int chr=str.charAt(i);
- if(chr<48 || chr>57)
- return false;
- }
- return true;
- }
4由于浮点数无法精确存储,引发的两个编程问题
浮点数是以IEEE754标准转化为二进制代码存储在计算机中的,他的这种存储编码导致了不管是float还是double都无法精确的存储一个浮点数 比如说 x=6.234; 逻辑上讲,那x就等于6.234,但是是不对的,浮点数的存储编码的缺陷导致真正存入计算机的可能是非常接近6.234的一个浮点数,比如6.23999999999 当然也可能是6.2340000000001 由于浮点数无法精确存储,引发了两个编程问题 1. 一个浮点型的变量X,怎么判断X值是否为0 ? 因为真正存入计算机的浮点数都是一个近似值 解决方法: if(|X-0.000001|<0.000001) 是0 else 不是0 2.为什么循环更新的变量不能定义成浮点型? float i; float sum=0; for(i=1.0;i<=100;++i){ // 如果 i 是浮点型,最后一次的i很可能是100.00000001 就少了一次循环 sum=sum+1/i; } 复制代码 逻辑上讲这段代码是没有错误的,但是由于浮点数无法在计算机中精确存储,所以这段代码就变的有问题了。。
三 final经典分析
1. final数据
对于基本数据类型的数据而言,final修饰符表示,该数据不会被修改。
对于非基本类型的对象引用而言,final修饰符所限定的引用恒定不变。一旦引用被初始化指向一个对象,就无法对其改变以指向另一个对象(然而,对象自身却是可以被修改的。Java并未提供任何使“对象”恒定不变的途径。这一限制同样适用于数组,它也是对象)。2. final方法---不能被重载
使用final方法的原因有两个。第一个原因是把方法锁定,以预防任何继承类修改它的意义。这是出于设计的考虑:你想要确保在继承中方法的行为不变,并且不会被重写。
使用final方法的第二个原因是效率。如果你将一个方法指明为final,就是同意编译器将针对该方法的所有调用都转为内嵌(inline)调用。当编译器发现一个final方法调用命令时,它会根据自己的谨慎判断,跳过插入程序代码的正常方式而执行方法调用机制(将参数压入栈中,跳至方法代码处并执行,然后跳回去并清除栈中的参数,处理返回值),并且以方法体中的实际代码的复本来替代方法调用。这将消除方法调用的开销。当然,如果一个方法很大,你的程序代码就会膨胀,你可能看不到内嵌带来的任何性能提高。因此,你所取得的性能提高会因为花费于方法内时间总量而被缩减。这意味着Java编译器能够观察到这些情况并明智的抉择是否对final方法执行内嵌动作。然而,最好是让编译器和JVM仅在你明确表示要阻止重写(override)时,再考虑效率问题,并将方法指明为final。
注:类中的private方法隐含为final方法,它不可被重写。因此,在private方法前加上final修饰符是没有意义的。
Java中除了static和final方法(private方法属于final)外,其他所有的方法都是后期绑定(动态绑定)。因此,将方法声明为final,可以有效的“关闭”动态绑定,或者是想告诉编译器,不需要对其进行动态绑定。这样,编译器就可以为final方法调用生成更有效的代码。然而,大多数情况下,这样做对程序的整体性能不会产生什么改观。
3. final类--不能被继承,final类中的方法默认为final
如果某个类整体被定义为final,则该类不可被继承,它不可有子类。这种设计可能是出于安全或其他方面的考虑。
final类中的所有方法都隐含为final类型的,因为final类本身是不可被继承的,所以类中的方法也不能被重写(override)。在final类中,你可以给方法添加final修饰符,但这不会增加任何意义四 字符串中NULL值与“”(空字符)的区别
(1)NULL
null 关键字是表示不引用任何对象的空引用的文字值。
null 是引用类型变量的默认值。那么也只有引用型的变量可以为NULL,如果 int i=null,的话,是不可以的,因为Int是值类型的。
string str2=null,这样定义后,只是定义了一个string 类的引用,str2并没有指向任何地方,在使用前如果不实例化的话,都将报错。(2)""和String.Empty (这两个都是表示空字符串,)
其中有一个重点是string str1="" 和 string str2=null 的区别,
str1是一个空字符串,空字符串是一个特殊的字符串,只不过这个字符串的值为空,在内存中是有准确的指向的,
string str2=null,这样定义后,只是定义了一个string 类的引用,str2并没有指向任何地方,在使用前如果不实例化的话,都将报错
判定为空字符串的几种写法,按照性能从高到低的顺序是:
s.Length == 0 优于 s == string.Empty 优于 s == ""判断字符串是否为空最好的方法就是 s.Length==0 !
五 Java是传值
栈是运行时的单位,而堆是存储的单位。 栈代表了处理逻辑,而堆代表了数据。
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
栈存的是逻辑,堆存的是数据部分。简单可以理解为一个对象的数据成员存于堆区,方法成员及其调用关系存于栈区。
对于基本类型数据的传递,传的就是其所表示的值
对于引用类型,都是通过位于栈区的引用变量来调用,传递的是引用变量表示的值,此值为引用变量所引用的对象在内存中的逻辑地址。
由此可知,Java是传值的。(真正的数值---or---地址值)
1 java程序只有传值,没有传引用,传地址的说法。但是传递的值可以是具体的数值,也可以是一个对象的引用。可以用这样一句话来描述“java函数是传值的,java函数传递的参数是对象的引用”。
传递参数的时候传递的就是相应内存地址中的数,所以说“java函数是传值的”。当然,这个数对于基本类型和对象类型来说意义是不一样的,对于基本类型这个数就是其值本身,传递值的结果就是,改变新的变量的值不影响旧的变量的值;而对于对象来说这个数是它的地址,传递这个值就相当于传递了真实对象的引用,传递了引用或者说是地址的结果就是变化会全局可见,所以又可以说“java函数传递的参数是对象的引用”。
2 一个栈内存只能指向一个堆内存空间,如果要想再指向其他的堆内存空间,则必须先断开已有的指向才能分配新的指向。
3 java中常用的内存区域: 在java中主要存在4块内存空间,这些内存的名称及作用如下
栈内存空间:保存所有的对象名称(更准确地说是保存了引用的堆内存空间的地址,局部变量)
堆内存空间:保存每个对象的具体属性内容。(引用变量、静态变量)
全局数据区:保存static类型的属性。
全局代码区:保存所有的方法定义。
4 《深入java虚拟机》一书的第五章
5 Java manipulates objects 'by reference,' but it passes object references to methods 'by value.'" As a result, you cannot write a standard swap method toswap objects
六 子类---父类 中成员变量的继承及this--super
访问变量看声明,访问方法看实际对象类型(new出来的类型)
子类从父类继承分成两种:
1.方法的继承:
方法的继承,父类直接把自己的方法转移到子类中去,当然前提是父类的方法修饰符的访问范围是子类可以访问的范围,但是如果子类已经重写了父类的方法,这时候这个方法就不能父类从转移到子类中。
2.成员变量的继承 :
成员变量就会有点奇妙了,它不会从父类转移到子类,而是保留在父类中,这就会出现,子类和父类可能同时拥有两个相同名字的变量。
下面用一段代码来说明这种相对比较复杂的关系:
package com.text;
public class Test {
public static void main(String[] args) {
Father a = new Father();
Chilren b = new Chilren();
Father c = new Chilren();
a.getAge(); 40
System.out.println(a.age); 40
b.getAge(); 18
System.out.println(b.age); 18
c.getAge(); 18
System.out.println(c.age); 40
}
}
class Father {
int age = 40;
public void getAge() {
System.out.println(age);
}
}
class Chilren extends Father {
int age = 18;
public void getAge() {
System.out.println(age);
}
}
输出
40
40
18
18
18
40
前四个结果可以理解,但是最后两个结果有人可能会问为什么结果会不一样,这段代码就体现了,成员变量继承和方法继承的区别。
可以得出结论:
Father c = new Chilren();
在以上父类引用指向子类对象情况下,访问变量看的是引用类型,所以c.age是父类的成员变量,而c.getAge()访问到的是子类Chilren的方法,所以在这个方法中用到的age变量是Chilren的变量。
反正一句话,访问变量看声明,访问方法看实际对象类型(new出
来的类型)
static方法与覆盖
静态方法只能被隐藏,不能被覆盖,隐藏表明还存在,还会起作用--子类隐藏父类的静态方法,仍会执行父类的静态方法.
3 this ---VS---super
接下来对代码做部分修改
?public static void main(String[] args) {
Chilren b = new Chilren();
Father c = b
System.out.println(b.age); 18
System.out.println(c.age); 40
}
输出
18
40
b 和c 两个引用都是指向内存中同一个对象,但是打印出来的结果却是不同,这就说明了,内存中保存了两个 age的值,一个是18 一个是40 。
这里就会产生一些疑问,在内存中他们是怎么存储的?这时候会想到有个super关键字,通过super. 可以访问到父类的变量和方法,这里有人会说:“super.代表的就是一个父类对象,因为他指向父类” 之前我也是这么想,但是看过一些书后知道其实不是这样
其实super.不是“东西”,说道super.自然会想到this.,有人把他们归为同类,其实他们大大不同
this:是一个真真实实对象,代表的就是当前对象,可以用 return this; 去返回一个对象。
super:不能一个对象,不是指向父类对象的意思,super只是修饰了他后边的内容,告诉JVM,后面这部分内容不是当前对象所属类的内容而已,若用return super,JVM是不允许的,是一种错误的语法。
public static void main(String[] args) {
Chilren b = new Chilren();
Father c = b
System.out.println(b.age);
System.out.println(c.age);
}
输出
18
40
回归到上面这段代码,这里并不是说内存中有两个对象 b 和 c ,内存中其实就只有一个 b对象 ,只是c 不仅有自己的实例 变量,同时也存在父类所定义的全部实例变量。
所以可以得出结论:在实例化一个子类的同时,系统会给子类所有实例变量分配内存,也会给他的父类的实例变量分配内存,及时父子类中存在重名的实例变量,也会两个都分配内存的,这个时候子类只是隐藏了父类的这个变量,但还是会给它分配内存,然后可以用super来访问属于父类的变量。
七java字符集和编码
1.为什么要编码
计算机本质上只能处理数值,每个字符在计算机中只能用一个整数来表示,这种整数称为字符编码。
2.编码格式
ASCII码
总共有 128 个,用一个字节的低 7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。
ISO-8859-1
ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,其中 ISO-8859-1 涵盖了大多数西欧语言字符,
所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。
GB2312
它的全称是《信息交换用汉字编码字符集 基本集》,只收录简体汉字,共有7445个字符。
机内码使得汉字编码的空间为0xA1A1到0xF7FE,每个汉字的编码为两个字节,每个字节的编码在0xA1到0xFE之间
ASCII的编码是一个字符一个字节,每个字节的编码在0x00到0x7F之间,这样就使得英文和汉字的编码空间不再重叠.
注:区号加位号可以作为一种编码,这种编码称为区位码.区位码不能直接在机器中使用,因为它的编码空间与ASCII重叠,
为了解决这个问题GB2312的字符在机器中实际采用的是机内码,通过一个简单的对应关系来实现两者之间的转换。
GBK
全称叫《汉字内码扩展规范》,GBK包含了GB2312的全部字符,同时还包括了Big5中的全部字符以及日文、韩文中使用的汉字。
对于原GB2312中的字符,GBK的编码就是原来的机内码。
GBK的编码空间从0x8140到0xFEFE,每个汉字的编码仍为两字节,其中第一个字节8位二进制数的最高位始终是1,
这使得GBK的编码空间可以避开ASCII的编码空间。
注:<问题>:虽然GB2312和GBK都有效的避开了和ASCII编码空间的重叠,但是并没有和其他一些编码(比如Big5)避开重叠,
这样的话有可能我的一段文字到别人那边就变成另外一段文字,并不是很明显的出现乱码。
解决这种问题的最好办法是统一编码,不管简体字还是繁体字,还有日文、韩文中使用的汉字,全部统一编码,
使得每个编码对应的汉字是唯一的,并且与世界上所有其它语言的字符编码不重叠,这就是Unicode。
Unicode编码:
编码方法是首先将所有的字符排列在若干个平面上,每个平面划分为256行,每行256列,从而每个平面存放64K(1K=1024)个字符。
每个字符所在的平面号、行号和列号称为码位(code point),码位可作为字符的编码。
平面0是最重要的平面,称为BMP(Basic Multilingual Plane),其中基本上包括了当今世界上各种语言使用的字符,
当然也包含中、日、韩三种文字中使用的所有汉字。
理论上ISO/IEC 10646预留了64K个平面,这样平面号的编码范围将为0x0000~0xFFFF,加上平面内的行号和列号,
字符码位的范围将是0x00000000~0xFFFFFFFF,也就是说,必须用4个字节来存放一个字符的码位。 Unicode仅使用编号0x00到0x10的17个平面,
其码位范围为0x000000~0x10FFFF,也需要用3个字节来存放。 但是我们实际使用的字符绝大多数都在BMP中,
只是偶尔使用其它平面的字符,因此可以设计一种两字节的编码,使得BMP中的字符编码就是它们在BMP中的码位,
而其它平面的字符用一种代替的方法来表示,这样的编码就是UTF-16。
UTF-16
UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,
不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。
UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,
这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。
UTF-8UTF-8采取不定长的编码方法:
ASCII字符的编码只用一个字节,并且其编码就是原来的ASCII码, 其它字符的编码从2个字节到4个字节不等。
每个字符编码的第一字节指出该字符编码的字节数:其中第一个二进制数0的左边有多少个1,该字符编码就有多少个字节;
如果第一位就是0,则该字符编码只有一个字节。
例如,某个字符编码第一字节是0xxxxxxx(x表示任意的二进制数),则该字符的UTF-8编码只有一个字节。
若某个字符编码第一字节为110xxxxx,则该字符的编码将有两个字节。对于两个字节及其以上的UFT-8编码,
从第二字节开始,每个字节开头两个二进制数都是10。
java中常用到编码的地方
用一句话描述一下编码:任何我们输入的字符当需要被计算机保存或者用于传输时都需要进行编码。
问题在于需不需要你去显示的编码<不知道准不准确>
1.内存中操作的编码
情况1:Java 中用 String 表示字符串,所以 String 类就提供转换到字节的方法,也支持将字节转换为字符串的构造函数。
1
String s =
"中国"
;
2
byte
[] b = s.getBytes(
"UTF-8"
);
3
String n =
new
String(b,
"UTF-8"
);
情况2:Charset 提供 encode 与 decode 分别对应 char[] 到 byte[] 的编码和 byte[] 到 char[] 的解码
1
Charset charset = Charset.forName(
"UTF-8"
);
2
ByteBuffer byteBuffer = charset.encode(string);
3
CharBuffer charBuffer = charset.decode(byteBuffer);
2.I/O 操作中存在的编码:I/O 包括磁盘 I/O 和网络 I/O (在转换流上注意编码的设置)
Reader 类是 Java 的 I/O 中读字符的父类,而 InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁, 它负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,
在 StreamDecoder 解码过程中必须由用户指定 Charset 编码格式。值得注意的是如果你没有指定 Charset,将使用本地环境中的默认字符集
写的情况也是类似,字符的父类是 Writer,字节的父类是 OutputStream,通过 OutputStreamWriter 转换字符到字节。
01
// 写字符换转成字节流
02
FileOutputStream outputStream =
new
FileOutputStream(
"c:/stream.txt"
);
03
OutputStreamWriter writer =
new
OutputStreamWriter(
outputStream,
"UTF-8"
);
05
try
{
06
writer.write(
"中国"
);
07
}
finally
{
08
writer.close();
09
}
10
// 读取字节转换成字符
11
FileInputStream inputStream =
new
FileInputStream(file);
12
InputStreamReader reader =
new
InputStreamReader(
13
inputStream, charset);
14
StringBuffer buffer =
new
StringBuffer();
15
char
[] buf =
new
char
[
64
];
16
int
count =
0
;
17
try
{
18
while
((count = reader.read(buf)) != -
1
) {
19
buffer.append(buffer,
0
, count);
20
}
21
}
finally
{
22
reader.close();
23
}
八 for-each循环优先于传统的for循环
集合遍历
1.利用for-each不会有性能的损失,在某些情况下,比起普通的for循环,它还稍有性能优势,因为它对数组索引的边界值只计算一次。
01
jdk1.
5
之前对集合和数组的遍历
02
for
(Iterator i=c.iterator();i.hasNext();){
03
dosomething((Element)i.next());
04
}
05
06
for
(
int
i=
0
;i<a.length;i++){
07
}
08
09
jdk1.
5
以后
10
for
(Element e:elements){}
2.在对多个集合进行嵌套迭代时,for-each循环相对应传统的for循环的这种优势会更加明显
如果使用for-each这个问题就完全消失了
1
enum
Face{one,two,three,four,five,six}
2
3
Collection<Face> faces=
new
Arrays.asList(Face.values);
4
for
(Iterator<Face> i=faces.iterator;i.hasNext();)
5
for
(Iterator<Face> j=faces.iterator;j.hasNext();)
6
system.out.println(i.next()+
" "
+j.next());
7
8
程序不会抛出异常,但不会完成你的工作,这种bug很难发现
1
for
(Face f1:faces)
2
for
(Face f2:faces)
3
system.out.println(f1+
""
+f2);
有几种情况无法使用for-each
1.替换:需要替换列表中的部分元素或全部元素
1
List<String> test=
new
ArrayList<String>();
2
test.add(
"aa"
);
3
test.add(
"bb"
);
4
for
(String s:test){
5
test.remove(s);
6
test.add(
"New_aa"
);
//ConcurrentModificationException
7
}
1
for
(
int
i=
0
;i<test.size();i++){
2
test.remove(i);
3
test.add(
"new"
);
4
}
5
//成功的替换
2.删除:如果要遍历,并删除指定的元素(并不是当前遍历到的元素),就需要显示的迭代器
3.迭代如果需要并行的遍历多个集合,就需要显示的控制迭代器或者索引变量,以便所有迭代器或者索引变量得到同步前移
1
如这段上面举例的问题代码
2
enum
Face{one,two,three,four,five,six}
3
4
Collection<Face> faces=
new
Arrays.asList(Face.values);
5
for
(Iterator<Face> i=faces.iterator;i.hasNext();)
6
for
(Iterator<Face> j=faces.iterator;j.hasNext();)
7
system.out.println(i.next()+
" "
+j.next());
九 Java深克隆浅克隆
深克隆:
拷贝对象的基本属性,包括其类型,拷贝对象的引用类型。
如果被克隆的对象有引用对象,那么经过深克隆后将产生两个对象。
浅克隆:
只拷贝对象的基本属性。
需要实现cloneable接口,重写clone方法,此方法一定要是public 否则你无法使用该克隆对象。
并且第一句一定要是super.clone();
十 Scanner类
Scanner类从字面上讲是“扫描”的意思,它把给定的字符串解析成Java的各种基本数据类型primitive types,用于分解字符串的默认的分隔符是空格,当然也可以定制。
例如:Scanner sc = new Scanner(System.in);
其构造函数参数是待解析的输入源,可以是File对象、Stream对象,或是一个String,然后还有java.lang.Readable对象。
定制分隔符的方法是sc. useDelimiterj(Pattern),然后使用while循环和sc.next()来依次取出Scanner解析后的元素,还可以特定取sc.nextInt()/ nextLong()/ nextShort()/ nextDouble()等等。
最后,不管输入源是不是Stream,都请执行sc.close()方法,关闭Scanner,它同时会关闭其输入源(the source implements the Closeable interface)。
import java.io.*; import java.util.*; public class ScanFar { public static void main(String[] args) throws IOException { Scanner sc = new Scanner(new BufferedReader(new FileReader("words.txt"))); //sc.useDelimiter("t");// 默认是空格 while (sc.hasNext()) { System.out.println(sc.next()); } sc.close(); } }
如果words.txt文件中的内容是:“So she went into the garden...” 那么结果如下,整个字符串按空格来分为了多个String对象So
she
went
into
the
garden...如果sc.useDelimiter("t"),那么结果按字母t来分解,如下:
So she wen
in
o
he garden...
发现被定义为分隔符的字母t被消除了,不作为结果的一部分。PS :
以前常常遇到要从字符串中取出某些部分,有了Scanner类,比Split()方便灵活多了。
注:sc.next()是从结果集中连续取值,如果要从一串字符中取出间插在中间的数字, 那么使用sc.nextInt(),但是如果结果集中的下一个元素不是int类型的话就会抛出异常,要达到这一目的, 在循环中添加if条件判断即可,如下:
While(sc.hasNext()){
if(sc.hasNextInt()){ // sc.hasNextShort()/hasNextDouble/…等各种基本数据类型
//做事件…
}
else{
next();//直接跳过
}}
十java生成随机密码的一个方法
- /**
- * 生成随机密码
- * @return
- */
- public static String makeRandNum() {
- String str = DateUtils.getCurrLongTime();
- long time = Long.parseLong(str.substring(6, 14));
- long num = (long) (Math.random() * 90000000L) + time + 100000L;
- // 判断生成的数
- String cardPass = String.valueOf(num);
- if (cardPass.length() > 8) {
- cardPass = cardPass.substring(0, 8);
- }else if(cardPass.length()<8){
- StringBuffer nameBuffer = new StringBuffer(cardPass.trim());
- while (nameBuffer.length() < 8) {
- nameBuffer.append("0");
- }
- cardPass=nameBuffer.toString();
- }
- return cardPass;
- }
十 fill方法
1.Arrays.fill(ai, 5);数组中的值都被替换成第二个参数指定的值。
2.将16个随机数填入数组 Array.fill(16, {100.rand})
3. public static void fill(Object[] a,
int fromIndex,
int toIndex,
Object val)将指定的 Object 引用分配给指定 Object 数组指定范围中的每个元素。填充的范围从索引
fromIndex(包括)一直到索引 toIndex(不包括)。(如果 fromIndex==toIndex,则填充范围为空。)
参数:
a - 要填充的数组
fromIndex - 要使用指定值填充的第一个元素的索引
a - 要填充的数组
fromIndex - 要使用指定值填充的第一个元素的索引
a - 要填充的数组
fromIndex - 要使用指定值填充的第一个元素的索引
a - 要填充的数组
fromIndex - 要使用指定值填充的第一个元素的索引 (包括)
toIndex - 要使用指定值填充的最后一个元素的索引
toIndex - 要使用指定值填充的最后一个元素的索引
toIndex - 要使用指定值填充的最后一个元素的索引
toIndex - 要使用指定值填充的最后一个元素的索引(不包括)
val - 要存储在数组的所有元素中的值
val - 要存储在数组的所有元素中的值
val - 要存储在数组的所有元素中的值
val - 要存储在数组的所有元素中的值
抛出:
IllegalArgumentException - 如果 fromIndex > toIndex
ArrayIndexOutOfBoundsException - 如果 fromIndex < 0 或 toIndex > a.length
ArrayStoreException - 如果指定值不是可存储在指定数组中的运行时类型
IllegalArgumentException - 如果 fromIndex > toIndex
ArrayIndexOutOfBoundsException - 如果 fromIndex < 0 或 toIndex > a.length
ArrayStoreException - 如果指定值不是可存储在指定数组中的运行时类型
IllegalArgumentException - 如果 fromIndex > toIndex
ArrayIndexOutOfBoundsException - 如果 fromIndex < 0 或 toIndex > a.length
ArrayStoreException - 如果指定值不是可存储在指定数组中的运行时类型