一、分支循环
??Java中,除了普通的顺序结构外,特殊的结构有三种:分支结构、循环结构和跳转语句。
1.1 分支结构
??if、if-else、if-else if-else结构适合某个变量值在连续区间 情况下的语句执行。 ??如果某个变量值不是在连续区间的话,比如是整数,可以用switch。switch基本语法:
switch(expression){
case value :
break;
case value :
break;
default :
}
??switch-case结构与多种if结构作用相似,但是有局限性,其特点:
- 1、JDK1.7之前的版本中,switch语句支持的数据类型有
byte、short、int、char和枚举类型 (即在整数类型中,唯独不支持long类型);在JDK1.7及以后的版本中,增加了对String类型的支持(编译后是把String转化为hash值,其实还是整数)。 - 2、
用于等值判断 。 - 3、break是每种case的结束标志,如无break则会继续走下个case。
- 4、一般最后都会有个default,用来处理匹配不到任何case的情况。
??switch使用示例:
int grade=80;
switch(grade){
case 60:
case 70:
System.out.println("你的成绩为60或70分");
break;
case 80:
System.out.println("你的成绩为80分");
break;
default:
System.out.println("你的成绩为60、70、80之外的分数");
break;
}
1.2 循环结构
??Java中循环结构有两种:
- for循环,有三部分:初始化操作、判断终止条件表达式及迭代部分。
for循环一般用于循环次数已知的情况 。 - while循环,有两种:
while(判断条件){
循环体
}
do{
循环体
}while(判断语句);
??do-while先执行循环体语句,然后进行判断,也就是无论如何会先执行一次循环体语句。
1.3 跳转语句
??跳转语句有三种:break、continue、return。此外,异常处理也可以改变程序执行流程。 ??break有以下作用:
- 在switch中,用来终止一个语句序列。
- 用来退出一个循环。
??continue只能用来for、while、do while循环中,用于跳过当前循环,直接进行下一次循环。 ??return语句使程序控制返回到调用它的地方,也就是:
- 返回方法的返回值;
- 终止当前程序。
??示例:
public class SkipTest {
public static void main(String[] args) {
int result = sum(10);
System.out.println("所求的数字之和是:"+result);
}
private static int sum(int num){
int s = 0;
for(int i=1;i<=num;i++){
if(i==3)
continue;
System.out.println("i:"+i);
s=s+i;
if(i==(num-1))
break;
}
return s;
}
}
??测试结果:
i:1 i:2 i:4 i:5 i:6 i:7 i:8 i:9 所求的数字之和是:42
??这段代码是一个序列的数字之和,不过不需要+3,所以在i == 3时,使用continue,表示跳过此次循环,进入下一次循环。我们也不希望+10,所以在i == (num-1)时,使用了break,直接跳出循环。return的作用就是返回到调用该方法的地方,有返回值的话将返回值返回,无返回值的话则不用返回。
1.4 switch是否能作用在byte上,是否能作用在long上,是否能作用在String上?
??在JDK1.5以前,switch(expr)中,expr 只能是 byte、short、char、int。 ??从JDK1.5开始,Java 中引入了枚举类型,expr也可以是enum类型。 ??从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的 。
- 为什么只支持上面几种?int、String 都可以,为什么不支持 long ?
原因就是 switch 对应的 JVM 字节码 lookupswitch、tableswitch 指令只支持 int 类型。 byte、char、short 类型在编译期默认提升为 int,并使用 int 类型的字节码指令。所以对这些类型使用 switch,其实跟 int 类型是一样的。
1.5 continue、break、和return的区别是什么?
??在循环结构中,当循环条件不满足或循环次数达到要求后,循环会正常结束。但是,有时候可能需要在循环的过程中,提前终止循环,这就是需要用到以下关键词:
- 1、continue
??指挑出当前的这一次循环,继续下一次循环。 - 2、break
??指跳出整个循环体,继续执行循环下面的语句。 - 3、return
??指跳出所在方法,结束该方法的运行。return一般有两种用法:
return; ,直接使用return结束方法执行,用于没有返回值函数的方法;return value; ,return一个特定值,用于有返回值函数的方法。
二、数组
??一个数组就代表在内存中开辟一段连续(意味着有序) 、用来存储相同类型 数据的空间,其特征如下:
- 数组名代表的是连续空间的首地址。
- 通过首地址可以依次访问数组所有元素,元素在数组中的位置叫做下标,从0开始。
- 数组长度一旦声明,不可改变不可追加。
2.1 声明数组
??数组的声明方式有两种,以int型数组举例:int[ ] array和int array[ ],一般用第一种方式。
2.2 创建数组
??数组的创建方式也有两种,以int型数组举例。
int[ ] array = new int[10]
?该语句的意思是创建一个容量大小为10的int型数组,数组中默认值是0。也就是说,当数组元素未赋值时,数组元素的值为数组元素类型的默认值(此处是int型,所以默认值是0)。
int[ ] array1 = {1,2,3,4,5,6};
int[ ] array2 = new int[ ]{1,2,3,4,5,6};
?这两个语句的意思都是创建一个容量大小为6的int型数组,数组中的初始值为1,2,3,4,5,6。
2.3 遍历数组
??数组的遍历方式也有两种,以int型数组举例。
for(int i=0;i<array.length;i++)
System.out.println(array[i]+" ");
for(int arr:array)
System.out.println(arr+" ");
2.4 数组作为方法参数/方法返回值
??数组可以作为参数传递给方法。示例:
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
??数组也可以数组作为函数的返回值。示例:
public static int[] reverse(int[] list) {
int[] result = new int[list.length];
for (int i = 0, j = result.length - 1; i < list.length; i++, j--) {
result[j] = list[i];
}
return result;
}
2.5 数组复制
??对数组进行复制的方法,常常有System.arraycopy和使用Arrays工具类两种方法,这两种方法在JDK源码中经常出现。此处介绍第一种,具体的方法是:
System.arraycopy(src, srcPos, dest, destPos, length)
?src: 源数组 ?srcPos: 从源数组复制数据的起始位置 ?dest: 目标数组 ?destPos: 复制到目标数组的起始位置 ?length: 复制的长度
??示例:
static int[] arr1 = {1,2,3,4,5,6};
static int[] arr2 = new int[6];
public static void main(String[] args) {
System.arraycopy(arr1, 0,arr2, 1, 3);
for(int i=0;i<arr2.length;i++) {
System.out.print(arr2[i] + " ");
}
}
2.6 多维数组
??多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组,格式:
type[][] typeName = new type[typeLength1][typeLength2];
??type 可以为基本数据类型和复合数据类型,typeLength1 和 typeLength2 必须为正整数,typeLength1 为行数,typeLength2 为列数。例如:
int[][] a = new int[2][3];
??二维数组 a 可以看成一个两行三列的数组。 ??给二维数组赋值的示例:
String[][] s = new String[2][];
s[0] = new String[2];
s[1] = new String[3];
s[0][0] = new String("Good");
s[0][1] = new String("Luck");
s[1][0] = new String("to");
s[1][1] = new String("you");
s[1][2] = new String("!");
三、String/StringBuffer和StringBuilder
??String(字符串),字符串是常量,它们的值在创建之后不能更改 。这个特性可以从String的底层实现看出:
private final char value[];
3.1 String的创建
??常常用来比较的两种创建字符串的方式:
String str1 = "one";
String str2 = new String("one");
??第一种直接赋值的方式,创建的对象是在常量池 ;第二种通过构造方法,创建的对象是在堆内存 。 ??JDK1.7 及之后版本的 JVM将运行时常量池从方法区中移了出来,在 Java 堆中开辟了一块区域存放运行时常量池。 ??这两种变量的比较方式也有两种:
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
??==比较的是对象的地址,str1对象和str2对象分别在常量池(常量池一般就是指字符串常量池,是用来做字符串缓存的一种机制 )和堆,所以用此方式比较的结果为false。equals比较的是对象的值,所以比较结果为true。
??在Object类中,equals()是用来比较内存地址的,但是String类中重写了equals()方法,用来比较内容。
??直接赋值创建字符串对象的方式,在创建多个相同内容的对象时,也只开辟一块内存空间,并且会自动入常量池,不会产生垃圾。通过构造方法创建字符串对象的方式,在创建多个相同内容的对象时,会开辟多个内存空间,并且创建的对象不在常量池里。因此,在实际开发过程中,都是使用直接赋值的方法创建字符串对象 。 ??在字符串使用equals比较时,一般把要比较的对象放前面,避免出现空指针问题,示例:
String str1 = "one";
System.out.println("two".equals(str1));
3.2 String常用方法
public char charAt(int index)
??示例:
String str1 = "abcdef";
System.out.println(str1.charAt(1));
??从上面可以看出使用charAt时,索引下标是从0开始的,这也从侧面说明了String的底层实现是数组。
public native int compareTo(String string)
??返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。示例:
String str1 = "one";
String str2 = "one";
String str3 = "on1two";
String str4 = "onetwo";
System.out.println(str1.compareTo(str2));
System.out.println(str1.compareTo(str3));
System.out.println(str1.compareTo(str4));
System.out.println(str3.compareTo(str4));
public String concat(String string)
??示例:
String str1 = "abcdef";
String str2 = "123";
System.out.println(str1.concat(str2));
public boolean startsWith(String prefix)
??示例:
System.out.println("abcde".startsWith("ab"));
public boolean endsWith(String suffix)
??示例:
String str1 = "abcdef";
String str2 = "123";
String str3 = "def";
System.out.println(str1.endsWith(str2));
System.out.println(str1.endsWith(str3));
public boolean equals(Object other)
??此处重写了Object的euqals方法,String中重写equals方法的步骤:
- 使用==操作符检查“实参是否为指向对象的一个引用”。
- 使用instanceof操作符检查“实参是否为正确的类型”。
- 把实参转换到正确的类型。
- 对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹配。
public boolean equalsIgnoreCase(String string)
??示例:
System.out.println("asd".equalsIgnoreCase("ASD"));
public int indexOf(int c)
??示例:
String str = "asd";
System.out.println(str.indexOf('s'));
public int indexOf(int c, int start)
??示例:
String str = "asdfg";
System.out.println(str.indexOf('s',2));
System.out.println(str.indexOf('s',0));
public int indexOf(String string)
??示例:
System.out.println("abcde".indexOf("cd"));
public int indexOf(String subString, int start)
??示例:
System.out.println("abcde".indexOf("cd",2));
public int lastIndexOf(int c)
??示例:
System.out.println("abcde".lastIndexOf('c'));
public int lastIndexOf(int c, int start)
??示例:
System.out.println("abcde".lastIndexOf('c',3));
public int lastIndexOf(String string)
??示例:
System.out.println("abcde".lastIndexOf("bc"));
public int lastIndexOf(String subString, int start)
??示例:
System.out.println("abcde".lastIndexOf("bc",2));
public int length()
??示例:
System.out.println("abcde".length());
public String[ ] split(String regularExpression)
??示例:
String str = new String("Y-s-t-e-n");
System.out.println("分隔符返回值 :" );
for (String s: str.split("-")){
System.out.println(s);
}
??测试结果:
分隔符返回值 : Y s t e n
??此处需要注意的是,使用split对字符串string进行分割字符串时,如果str中没有符合规则的,就返回完整的str字符串 。示例:
String str = new String("Y-s-t-e-n");
System.out.println("分隔符返回值 :" );
for (String s: str.split("2")){
System.out.println(s);
}
??结果:
分隔符返回值 : Y-s-t-e-n
public String substring(int start)
??示例:
System.out.println("abcde".substring(1));
- 19、截取子字符串,从start(包含)到end(不包含),即区间是前开后闭
public String substring(int start, int end)
??示例:
System.out.println("abcde".substring(1,3));
public String trim()
??示例:
System.out.print(" asdfg ".trim());
public static String valueOf(primitive data type x)
??示例:
long l1 = 1001L;
int i1 = 123;
System.out.println(String.valueOf(l1));
System.out.println(String.valueOf(i1));
public String toLowerCase()
public String toUpperCase()
3.3 String拼接原理
??String :常量,不可变,不适合用来字符串拼接,每次都是新创建的对象,消耗较大; ??StringBuffer :适合用来作字符串拼接,线程安全 ; ??StringBuilder :JDK1.5引入,适合用来作字符串拼接,与StringBuffer区别是线程不安全 的。
??从上面也能看出,String拼接时,中间会产生StringBuilder对象(JDK1.5之前产生StringBuffer)。具体的原理是:
??两个字符串str1, str2拼接时,首先会调用 String.valueOf(obj)方法,这个obj为str1,然后产生StringBuilder, 调用的StringBuilder( )构造方法, 把StringBuilder初始化,长度为str1.length()+16,并且调用append(str1)。接下来调用StringBuilder.append(str2), 把第二个字符串拼接进去, 然后调用StringBuilder.toString()返回结果。
3.4 String相关问题
3.4.1 什么是字符串常量池?
??字符串常量池位于堆内存中,专门用来存储字符串常量 ,可以提高内存的使用率,避免开辟多块空间存储相同的字符串。在创建字符串时,JVM会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。
3.4.2 String有哪些特性?
- 1、不变性
??String 是只读字符串,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。 - 2、常量池优化
??String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。 - 3、final
??使用final来定义String类,表示String类不能被继承,提高了系统的安全性。String类利用了final修饰的char类型数组存储字符,源码:
private final char value[];
3.4.3 为什么String在Java中是不可变的?
??String是Java中一个不可变的类,所以他一旦被实例化就无法被修改。不可变类的实例一旦创建,其成员变量的值就不能被修改。不可变类有很多优势。本文总结了为什么字符串被设计成不可变的。将涉及到内存、同步和数据结构相关的知识。
- 1、字符串池
??字符串池(String intern pool)是方法区的特殊存储区域。创建字符串时,如果池中已存在该字符串时,将返回现有字符串的引用,而不是创建新对象。 - 2、缓存Hashcode
??Java中经常会用到字符串的哈希码(hashcode)。例如,在HashMap或HashSet中,字符串的不可变能保证其hashcode保持一致,这样就可以避免一些不必要的麻烦。这也就意味着每次在使用一个字符串的 hashcode 的时候不用重新计算一次,这样更加高效。在String类中,有以下这段代码:
private int hash;
??此处的 hash 变量就是为了保存了String对象的hashcode,因为String类不可变,所以一旦对象被创建,该hash值也无法改变。所以,每次想要使用该对象的hashcode的时候,直接获取即可。
HashSet<String> set = new HashSet<String>();
set.add(new String("a"));
set.add(new String("b"));
set.add(new String("c"));
for(String a: set)
a.value = "a";
??如果字符串可以被改变,那么以上用法将有可能违反Set的设计原则,因为Set要求其中的元素不可以重复。上面的代码只是为了简单说明该问题,其实String类中并没有value这个字段值。
- 4、安全性
??String被广泛的使用在其他Java类中充当参数。比如网络连接、打开文件等操作。如果字符串可变,那么类似操作可能导致安全问题。因为某个方法在调用连接操作的时候,他认为会连接到某台机器,但是实际上并没有(其他引用同一String对象的值修改会导致该连接中的字符串内容被修改)。可变的字符串也可能导致反射的安全问题,因为他的参数也是字符串。 ??String 在其他 Java 类中被广泛作为参数使用,比如网络连接、打开文件等操作。如果字符串可变,那么类似这样的操作可能导致安全问题。比如,当某个方法在调用连接操作时,他会认为连接到了它已知的某台机器,但实际并没有。因为引用同一String对象的值被修改了。同时可变字符串也可能在Reflection中引起安全问题,因为参数是字符串。 ??示例:
boolean connect(string s){
if (!isSecure(s)) {
throw new SecurityException();
}
causeProblem(s);
}
- 5、不可变对象自然是线程安全的
??由于无法更改不可变对象,因此可以在多个线程之间自由共享它们,不存在同步的问题。
??总之,String被设计成不可变的主要目的是为了安全和效率。
3.4.4 常量池的使用
?示例:
String s1="a"+"b"+"c";
String s2="abc";
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
??创建s1对象时,就在常量池中产生了一个"abc"字符串,所以再用赋值的方式创建相同内容的字符串时,就直接使用常量池中,不再重复创建,所以两种比较方式的结果都是true,因为本就是一个对象。
3.4.5 常规字符串拼接
?示例:
String s1="ab";
String s2="abc";
String s3=s1+"c";
System.out.println(s3==s2);
System.out.println(s3.equals(s2));
??结果分析:s2是创建在常量池中的,s3是通过字符串拼接生成的,底层是通过StringBuilder(或 StringBuffer)对象实现的,这样产生的对象是存在堆内存中的。所以两者的地址是不一样的,==比较结果是false;内容是一样的,equals比较结果是true。
3.4.6 特殊字符串拼接
?示例:
String s = null;
s += "abc";
System.out.println(s);
s += null;
System.out.println(s);
??结果分析:字符串拼接时,会先调用String.valueOf(Object obj)来将对象转换为字符串,该方法的实现是:
public static String valueOf(Object value) {
return value != null ? value.toString() : "null";
}
??所以在字符串拼接时,会先判断是不是null,是null的话,会先转换成"null",而不是""进行拼接。 ??字符串常量池是在编译期确定好的,一次性创建一个完整的字符串 (String s1="zxc"或String s2=“z”+“xc”)时会去常量池查找 。 ??字符串拼接(如:str+"asd")是在运行期确定的,不会去常量池查找。此时新的对象会分配到堆内存中 ,字符串拼接的实际过程:
String s2 = new StringBuilder(str).append("asd").toString()
3.4.7 String s=new String(“abc”) 创建了几个对象?
??答案是一个或两个。new String()的形式无疑是会生成一个String对象的,接下来就看下String的构造方法:
public String(String original)
??从上面代码可以看出,该构造方法中的参数也是一个String对象,相当于是:
String str = "abc";
String s=new String(str) ;
??这样来看这道题就容易理解了,当字符串常量池中有“abc”时,就不会产生新的相同内容的对象;反之会创建,所以该问题的答案是一个或两个。
3.4.8 equals与==的区别
??Java对于eqauls方法和hashCode方法是这样规定的:
- 如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;
- 如果两个对象的hashCode相同,它们并不一定相同。
??在上面介绍的String类中,重写了equals方法,那么在日常开发中,怎么使用这两者呢? ??‘==’ 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作,比较规则有三:
- 比较的是操作符两端的操作数是否是同一个对象。
- 两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。
比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true ,如:int a=10 与 long b=10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。
??equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。 ??总的来说,要进行比较时,参考有二:
- 自定义类中尽量重写equals方法。
在使用equals进行比较时,把常量写在前面,因为常量与变量比较时,变量可能为null,此时会出现空指针异常 。
3.4.9 String str="i"与 String str=new String(“i”)一样吗?
??不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中 。
3.4.10 在使用HashMap的时候,用String做key有什么好处?
??HashMap内部实现是通过key的hashcode来确定value的存储位置,因为字符串是不可变的,所以当创建字符串时,它的hashcode被缓存下来,不需要再次计算,所以相比于其他对象更快。
3.5 StringBuffer
??StringBuffer是可变字符序列,它是一个类似于String的字符串缓冲区,可以装很多字符串,并且能够对其中的字符串进行各种操作。 ?? StringBuffer是线程安全的,因为其对外提供的方法都有synchronized关键字修饰。 StringBuffer常见方法:
public StringBuffer() {
}
public StringBuffer(String string) {
super(string);
}
??无参StringBuffer构造方法,默认开辟16个字符的长度的空间。
public synchronized StringBuffer append(String string)
??示例:
String str = "abc";
StringBuffer sb = new StringBuffer(str);
System.out.println(sb.append("def"));
public synchronized StringBuffer insert(int index, String string)
??示例:
String str = "abc";
StringBuffer sb = new StringBuffer(str);
System.out.println(sb.insert(3,"hhh"));
public synchronized String toString()
3.6 StringBuilder
?? StringBuilder是JDK1.5提出来的。StringBuilder是可变字符序列,也是一个类似于String的字符串缓冲区,可以装很多字符串。并且能够对其中的字符串进行各种操作。 ?? StringBuilder是线程不安全的。 tringBuilder常见方法:
public StringBuilder() {
}
public StringBuilder(String str) {
super(str);
}
public StringBuilder append(String str)
??示例:
String str = "abc";
StringBuilder sb = new StringBuilder(str);
System.out.println(sb.append("qwe"));
public StringBuilder insert(int offset, String str)
??示例:
String str = "abc";
StringBuilder sb = new StringBuilder(str);
System.out.println(sb.insert(2, "qwe"));
public String toString()
3.7 String、StringBuffer和StringBuilder的比较
??三者共同之处:都是final类,不允许被继承。
| String | StringBuffer | StringBuilder | 说明 |
---|
长度是否可变 | 不可变 | 可变 | 可变 | StringBuffer和StringBuilder可以通过append等方法改变长度 | 运行速度 | 慢 | 较慢 | 最快 | String最慢的原因:String为字符串常量; StringBuilder和StringBuffer均为字符串变量,可更改 | 线程是否安全 | 是(因为是字符串常量) | 线程安全 | 线程不安全 | | 适用情况 | 适用于少量的字符串操作的情况 | 多线程下在字符缓冲区进行大量操作的情况 | 单线程下在字符缓冲区进行大量操作的情况 | 实际上,StringBuffer的适用场景非常少,虽然它的线程安全,也仅仅是Jvm不抛出异常顺利往下执行,不能保证逻辑正确和调用顺序正确,大多数时候,需要的不仅仅是线程安全,而是锁 |
四、方法
??方法是语句的集合,它们在一起执行一个功能;方法是解决一类问题的步骤的有序组合;方法包含于类或对象中。 ??方法的命名一般用驼峰命名法,即第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写。 ??方法的优点:
- 使程序变得更简短而清晰。
- 有利于程序维护。
- 可以提高程序开发的效率。
- 提高了代码的重用性。
4.1 方法的定义
??一般情况下,定义一个方法包含以下语法:
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
??方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
- 修饰符
??修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。 - 返回值类型
??方法可能会返回值。returnValueType:方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType是关键字void。 - 方法名
??是方法的实际名称。方法名和参数表共同构成方法签名。 - 参数类型
??参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。 - 方法体
??方法体包含具体的语句,定义该方法的功能。
4.2 变量作用域
??变量的范围是程序中该变量可以被引用的部分。 ??方法内定义的变量被称为局部变量 。局部变量的作用范围从声明开始,直到包含它的块结束。局部变量必须声明才可以使用。 ??方法的参数范围涵盖整个方法。参数实际上是一个局部变量。 ??最常见的局部变量是:for循环的初始化部分声明的变量,其作用范围在整个循环。但循环体内声明的变量其适用范围是从它声明到循环体结束。它包含如下所示的变量声明: 你可以在一个方法里,不同的非嵌套块中多次声明一个具有相同的名称局部变量,但你不能在嵌套块内两次声明局部变量。
五、常用类
5.1 Math类
??Math类包含用于执行基本数学运算的方法。最常用的方法是数值计算类方法。
public static double abs(double d)
public static float abs(float f)
public static int abs(int i)
public static long abs(long l)
- 2、对一个数进行上舍入,返回值大于或等于给定的参数,类型为双精度浮点型
public static native double ceil(double d)
??示例:
double d = 10.6;
float f = -95;
System.out.println(Math.ceil(d));
System.out.println(Math.ceil(f));
- 3、对一个数进行下舍入,返回给定参数最大的整数,该整数小于或等给定的参数
public static native double floor(double d)
??示例:
double d = 10.6;
float f = -95;
System.out.println(Math.floor(d));
System.out.println(Math.floor(f));
public static native double log(double d)
??示例:
System.out.printf("log(%.3f) 为 %.3f%n",
Math.E*Math.E,Math.log(Math.E*Math.E));
public static native double log10(double d)
??示例:
System.out.printf("log(%.3f) 为 %.3f%n",
100.0,Math.log10(100.0));
public static double max(double d1, double d2)
public static float max(float f1, float f2)
public static int max(int i1, int i2)
public static long max(long l1, long l2)
public static double min(double d1, double d2)
public static float min(float f1, float f2)
public static int min(int i1, int i2)
public static long min(long l1, long l2)
public static native double pow(double x, double y)
??示例:
double d1 = 10.0;
double d2 = 2.0;
System.out.println(Math.pow(d1,d2));
- 9、返回一个随机数,随机数范围为 [0.0 ,1.0]
public static synchronized double random()
- 10、“四舍五入”,算法为Math.floor(x+0.5) ,即将原来的数字加上 0.5 后再向下取整
public static long round(double d) / static int round(float f)
??示例:
System.out.println(Math.round(11.5));
System.out.println(Math.round(-11.5));
public static native double sqrt(double d)
5.2 Arrays类
?? Arrays是一个封装好一些对数组操作的类,其中的public方法都是静态的,主要有以下几大类:
- 1、搜索类
?该类方法的作用是在数组中搜索某个值,返回该值在数组中的下标。该类方法的实现是二分搜索 ,例如在int数组中搜索某个值,方法实现代码:
public static int binarySearch(int[] array, int startIndex, int endIndex, int value) {
checkBinarySearchBounds(startIndex, endIndex, array.length);
int lo = startIndex;
int hi = endIndex - 1;
while (lo <= hi) {
int mid = (lo + hi) >>> 1;
int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid;
}
}
return ~lo;
}
- 2、赋值类
?该类方法的作用是给数组中元素赋予默认值,实现是遍历赋值。方法实现代码:
public static void fill(int[] array, int start, int end, int value) {
Arrays.checkStartAndEnd(array.length, start, end);
for (int i = start; i < end; i++) {
array[i] = value;
}
}
- 3、计算哈希值类
?该类方法的作用是计算数组中元素的哈希值。方法实现代码:
public static int hashCode(int[] array) {
if (array == null) {
return 0;
}
int hashCode = 1;
for (int element : array) {
hashCode = 31 * hashCode + element;
}
return hashCode;
}
- 4、比较值类
?该类方法的作用是比较两个数组是否相等。 - 5、排序类
?该类方法的作用是对数组中的元素进行排序,用到的排序方式是快速排序 。方法实现代码:
public static void sort(int[] array, int start, int end) {
DualPivotQuicksort.sort(array, start, end);
}
- 6、转换为字符串类
?即toString方法。 - 7、拷贝类
?该类方法的作用是拷贝数组中的元素,可以指定起始位置,未指定初始位置的话,默认从0开始。方法实现代码:
public static int[] copyOf(int[] original, int newLength) {
if (newLength < 0) {
throw new NegativeArraySizeException(Integer.toString(newLength));
}
return copyOfRange(original, 0, newLength);
}
int[] datas = {1,2,3,4,5,6};
List list = Arrays.asList(datas);
5.3 Collections类
??Collections类是针对集合操作的工具类。
public class Collections extends Object
??常用方法:
- 1、排序,默认是自然排序(排序算法是修改的归并排序算法)
public static void sort(List list)
??使用默认排序规则和自定义规则的示例:
Collections.sort(list);
Collections.sort(list,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if(o1.length()>o2.length()){
return 1;
}else if(o1.length()<o2.length()){
return -1;
}else{
return 0;
}
}
});
public static int binarySearch(List<?> list,T key) ? ?
public static T max(Collection<?> coll)
public static T min(Collection<?> coll)
public static void reverse(List<?> list)
5.4 Object类
5.4.1 Object常用方法
??Object是是所有类的父类,如果一个类没有用extends明确指出继承于某个类,那么它默认继承Object类 。JDK1.8版本中,Object类中的一些方法:
- 1、构造方法
??Object类中没有显示的提供构造方法,由编译器默认提供。 - 2、getClass
public final native Class<?> getClass();
??此方法返回类运行时的类型,示例:
public class JavaTest {
public static void main(String[] args) {
System.out.println((new JavaTest()).getClass());
}
}
public native int hashCode();
??示例:
public class JavaTest {
public static void main(String[] args) {
System.out.println((new JavaTest()).hashCode());
}
}
??该方法返回对象的哈希码,是一个整数。这个方法遵守以下三个规则:
- 1、在java程序运行期间,若用于equals方法的信息或者数据没有修改,同一个对象多次调用此方法,返回的哈希码是相同的。
- 2、如果根据equals方法,两个对象相同,则这两个对象的哈希码一定相同。
- 3、假如两个对象通过equals方法比较不相同,那么这两个对象调用hashCode也不是要一定不同,相同也是可以的。
??在实际使用中,要尽量保证对于不同的对象产生不同的哈希码。hashCode的典型实现是将对象的内部地址转为一个整数,但是这种实现技术不是Java语言必须要采用的。
public boolean equals(Object obj) {
return (this == obj);
}
??equals方法主要是比较两个对象是否相同,Object中的equals方法比较的是对象的地址是否相同。 ??在实际开发中,对于实体类,一般要重写equals(比较对象的内容而不是地址)和hashCode(尽量让不同的对象产生不同的哈希值)方法。
protected native Object clone() throws CloneNotSupportedException;
??浅拷贝一个对象。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
??示例:
public class JavaTest {
public static void main(String[] args) {
System.out.println((new JavaTest()).toString());
}
}
??可以看到Object中toString方法的实现是返回类的名称(全路径名称)加上@,然后加上此类的哈希码的16进制表示。
- 7、wait
??在Object中存在三种wait方法:
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
public final void wait() throws InterruptedException {
wait(0);
}
??wait()和wait(long timeout, int nanos)都在在内部调用了wait(long timeout)方法。这个方法是线程方面的,在线程相关文章会详细介绍。
public final native void notify();
public final native void notifyAll();
??这两个方法是唤醒线程,也会在后续线程相关文章中详细介绍。
5.4.2 hashCode与equals 【重要】
??hashCode() 的作用是获取哈希码(散列码);它实际上是返回一个int型整数。这个哈希码的作用是确定该对象在哈希表中的索引位置 。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。 ??散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象) ??hashCode()与equals()的相关规定:
1、如果两个对象相等,则hashcode一定也是相同的; 2、两个对象相等,对两个对象分别调用equals方法都返回true; 3、两个对象有相同的hashcode值,它们也不一定是相等的。
??因此,equals方法被覆盖过,则hashCode方法也必须被覆盖 。
- Java中的hashCode()的作用
??hashCode()的作用是为了提高在散列结构存储中查找的效率,在线性表中没有作用;只有每个对象的 hash 码尽可能不同才能保证散列的存取性能,事实上Object 类提供的默认实现确实保证每个对象的 hash 码不同(在对象的内存地址基础上经过特定算法返回一个 hash 码)。 ??在 Java 有些集合类(HashSet)中要想保证元素不重复可以在每增加一个元素就通过对象的 equals 方法比较一次,那么当元素很多时后添加到集合中的元素比较的次数就非常多了,也就是说如果集合中现在已经有 3000 个元素则第 3001 个元素加入集合时就要调用 3000 次 equals 方法,这显然会大大降低效率,于是 Java 采用了哈希表的原理,这样当集合要添加新的元素时会先调用这个元素的 hashCode 方法就一下子能定位到它应该放置的物理位置上(实际可能并不是),如果这个位置上没有元素则它就可以直接存储在这个位置上而不用再进行任何比较了,如果这个位置上已经有元素了则就调用它的 equals 方法与新元素进行比较,相同的话就不存,不相同就散列其它的地址,这样一来实际调用 equals 方法的次数就大大降低了,几乎只需要一两次,而 hashCode 的值对于每个对象实例来说是一个固定值。
|