①类名、接口名首字母大写,后面每个单词首字母大写,遵守驼峰命名方式;
②变量名、方法名首字母小写,后面每个单词首字母大写,也是遵守驼峰命名方式;
③常量名全部大写,单词和单词之间使用“_”衔接,为了表示清楚含义,不要怕单词长,例如:INT_MAX_VALUE表示int类型最大值。
一共有8种基本类型,其中有4种整型、2种浮点类型、1种字符类型char和boolean类型.
类型 |
存储需求 |
取值范围 |
缺省默认值 |
byte(字节型) |
1字节 |
-128 ~ 127 |
0 |
short(短整型) |
2字节 |
-32 768 ~ 32 767 |
0 |
int(整型) |
4字节 |
-2 147 483 648 ~ 2 147 483 647(刚刚过20亿) |
0 |
long(长整型) |
8字节 |
-9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807 |
0L |
类型 |
存储需求 |
取值范围 |
缺省默认值 |
float(单精度) |
4字节 |
大约±3.402 823 47E+38F(有效位数6~7位) |
0.0F |
double(双精度) |
8字节 |
大约±1.797 693 134 862 315 70E+308(有效位数15位) |
0.0 |
没有后缀F的浮点数值(如3.13)总是默认是double类型。
浮点类型不适用于无法接受收入误差的金融计算。例如2.0-1.1将打印出0.8999999999999999,而不是期望的0.9。这种舍入误差的主要原因是浮点数值采用二进制系统表示,而在二进制系统中无法精确的表示0.1。如果在数值计算中不允许舍入误差,就应该使用BigDecimal类。
类型 |
存储需求 |
取值范围 |
缺省默认值 |
char(字符型) |
2字节 |
0 ~ 65535 |
'\u0000' |
最初采用ASCII码,后国际标准组织又制定了ISO-8859-1字符集(主要支持西欧语言)。再后来,国际组织制定了一种叫做Unicode字符编码方式。
这种编码方式统一了全球所有国家的文字,具体的实现包括UTF-8,UTF-16,UTF-32等。Unicode转义序列会在解析代码之前得到处理。如,“\u0022+\u0022”并不是一个由引号包围加号构成的字符串。
更隐秘的是,在注释中的\u依然又可能产生这样的错误。例如//look inside c:\users 也会产生一个语法错误,因为\u后面并没有跟着4个十六进制数。
建议不要在程序中使用char类型,除非确实需要处理UTF-16代码单元。最好将字符串作为抽象数据类型处理。
字符编码涉及到编码和解码两个过程,编码和解码的时候必须采用同一套字符编码方式,不然就会出现乱码。
类型 |
存储需求 |
缺省默认值 |
boolean(布尔型) |
1字节 |
false |
在Java中,变量的声明尽可能地靠近变量第一次使用的地方,声明一个变量之后,必须使用赋值语句对变量进行显式初始化。变量实在内存中开辟出一个空间用来存放对应数据类型的数据并给他一个变量名用以后续调用。
基本类型的变量如果是临时变量,只要定义了,就会分配内存空间,不管是否被赋值;如果是作为对象的属性出现,只要该对象不实例化,就不会分配内存空间。
利用关键字final指示。关键字final表示这个变量只能被赋值一次。一旦被赋值之后,就不能够再更改了。
有时候,变量的取值只在一个有限的集合内。例如,销售的服装只有S、M、L、X。但这种设置很容易出错,很可能在变量中保存的是一个错误的值(m),针对这种情况。可以自定义枚举类型。枚举编译之后也是class文件,枚举类型包括有限个命名的值,例如:
enum Size{SAMLL,MEDIUNM,LARGE,EXTRA_LARGE};
现在可以声明这种类型的变量:
Size s = Size.MEDIUM;
Size类型的变量只能存储这个类型声明中给定的某个枚举值。或者特殊值null,null表示这个变量没有设置任何值。
实际上,这个声明定义的类型是一个类,它刚好有4个实例,不可能构造新的对象。因此,在比较两个枚举类型的值时,并不需要调用equals。直接使用“==”就可以了。
如果需要的话,可以为枚举类型增加构造器、方法和字段。当然,构造器只是在构造枚举常量的时候调用。下面是一个例子:
enum Size{
SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");
private String abbreviation; //实例字段:缩写
private Size(String abbreviation){this.abbreviation = abbreviation;}
public String getAbbreviation(){return abbreviation;}
}
具体调用:
public class EnumTest01 {
public static void main(String[] args) {
Size s1 = Size.SMALL;
System.out.println("s1 = "+s1); //s1 = SMALL
Size s = Enum.valueOf(Size.class, "LARGE");
System.out.println("s = " + s); //s = LARGE
Size[] array = Size.values();
for (int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
System.out.println(Size.MEDIUM.getAbbreviation()); //M
}
}
枚举的构造器总是私有的。所有的枚举类型都是Enum类的子类。它们继承了这个类的许多方法。其中最有用的一个是toString,这个方法会返回枚举常量名。例如,
Size.SMALL.toString(); //将返回字符串“SMALL”。
toString的逆方法是静态方法valueOf。例如,以下语句:
Size s = Enum.valueOf(Size.class, “SMALL”); //将s设置成Size.SMALL。
每个枚举类型都有一个静态的values方法,他将返回一个包含全部枚举值的数组。例如,如下调用:
//返回包含元素Size.SMALL、Size.MEDIUM、Size.LARGE、Size.EXTRA_LARGE的数组
Size[] values = Size.values();
ordinal方法返回enum声明中枚举常量的位置,位置从0开始计数。例如:
Size.MEDIUM.ordinal(); //返回1。
当参与“/”运算的两个操作数都是整数时,表示整数除法;否则,表示浮点除法。
需要注意,整数被0除将会产生一个异常,而浮点数被0除将会得到无穷大或NaN结果。
在Math类中,包含了各种各样的数学函数。在那些不同类别的程序种,可能需要的函数也不同。
不必在数学方法名和常量名前添加前缀“Math”,只要在源文件的顶部加上下面这行代码就可以了:
import static java.lang.Math; //静态导入
Math类提供了一些方法是整数有更好的运算安全性。如果一个计算溢出,数学运算符只是悄悄地返回错误的结果而不作任何提醒。例如,10亿乘以3的结果是-1294967296,因为最大的int值也只是刚过20亿。不过如果调用“Math.multiplyExact”就可以捕获异常。
Java语法规定,当++运算符出现在变量后,会先做赋值运算,再自加1。前缀形式(++m)会先完成+1;而后缀形式会先使用变量原来的值。
操作符 |
例子 |
结果 |
描述 |
^ |
true ^ false |
TRUE |
异或运算符,只要两边的操作数不同,结果就是true |
6个实线箭头表示无信息丢失的转换;另外有3个虚线箭头,表示有精度损失的转换。(当int转换为float时,是由准确值变成了近似值,比如int 的1000,转换为浮点型时,可能会被存成1000.00000000001)
当用一个二元运算符连接两个值时,先要将两个操作数转换为同一种类型,然后再进行计算。
如果两个操作数中有一个是double类型,另一个操作数就会转换为double类型;
否则,如果其中一个操作数是float类型,另一个操作数将会转换为float类型;
否则,如果其中一个操作数是long类型,另一个操作数就会转换为long类型;
否则,两个操作数都将被转换为int类型。
大容量转换成小容量,好比一大玻璃杯中的水倒向小玻璃杯,最终的结果可能会使水溢出,因为小玻璃杯可能放不下。编译器检测到这种情况的时候就不会自作主张了,需要程序员来指定,程序员必须手动添加强制类型转换符才能编译通过,这个过程我们称为强制类型转换。
double x = 9.997;
int nx = (int) x; //x = 9
因为强制类型转换通过截断小数部分将浮点值转换为整型。如想对浮点数进行舍入运算,以便得到最接近的整数,就需要使用Math.round方法:
int nx = (int) Math.round(x); //x = 10
当调用round的时候,仍然需要使用强制类型转换(int)。其原因是round方法返回结果为long类型,由于存在信息丢失的可能性,所以只有使用显式的强制类型转换才能够将long类型转换为int 类型。
当一个整数型的字面量没有超出 byte,short,char 的取值范围,可以将该字面量直接赋值给byte,short,char 类型的变量,如果超出范围则需要添加强制类型转换符。
如果试图将一个数值从一种类型强制转换为另一种类型,而又超出了目标类型的表示范围,结果就会截断成一个完全不同的值。
int a = 150;
byte b = (type)a; // b = -106
String类的substring方法可以从一个较大的字符串中提取出一个子串。例如:
String greeting = “Hello”;
String s = greeting.substring(0,3); //Hel
greeting = greeting.substring(0,3)+”p!”; //Help!
substring方法的第二个参数是不想复制的第一个位置。这里要复制为0、1、2。substring容易计算子串的长度。字符串s.substring(a,b)的长度为b-a。可以提取想要保留的子串,再与希望替换的字符拼接。
把多个字符串放在一起,用一个界定符分割,可以使用静态join方法:
String all = String.join(“/”,”S”,”M”,”L”,”XL”); //输出结果为”S/M/L/XL
可以使用equals方法检测两个字符串是否相等,表达式:
"Hello Word!".equals(str);
要想检测两个字符串是否相等,而不区分大小写,可以使用equalsIgnoreCase方法:”
"Hello".equalsIgnoreCase(str);
并且String类已经重写了toString()和equals()方法。
空串””是长度为0的字符串,有自己的串长度(0)和内容(空)。String变量还可以存放一个特殊的值,名为null,表示目前没有任何对象与该变量关联。
源代码中String类中有一个byte[]数组,这个byte数组采用了final修饰,因为数组一旦创建长度不可变,并且被final修饰的引用一旦指向某个对象之后,不可再指向其他对象,所以String是不可变的。有些时候,需要由较短的字符串构建长字符串,每次拼接字符串时,都会构建一个新的String对象,既耗时,又浪费空间。使用StringBuilder类就可以避免。
StringBuilder builder = new StringBuilder();
当每次需要添加一部分内容时,就调用append方法。
builder.append(ch);
2.6.6 String的存储原理
String表示字符串类型,属于引用类型数据,不属于基本数据类型。凡是双引号括起来的字符串存储在字符串常量池中。因为在实际开发中使用太频繁,为了执行效率所以把字符串放到了方法区的字符串常量池中。
下面这段代码实际上在方法区字符串常量池中创建了3个对象
String s1 = "abc";
String s2 = "abc" + "def";
凡是new对象的时候一定是在堆内存中开辟空间。
String s3 = new String(“def”);
byte[] bytes={97,98,99};//97是a,98是b,99是c
char[] chars={'我','是','程','序','员'};
String s2=new String(bytes);
String s3=new String(chars);
System.out.println(s2); //abc(输出一个引用时会自动调用toString()方法)
System.out.println(s3); //我是程序员
String s4=new String(bytes,1,2);
String s5=new String(chars,2,3);
System.out.println(s4); //bc
System.out.println(s5); //程序员
只要采用双引号直接赋值字符串,那么在编译期将会放到方法区中的字符串的常量池里。如果是运行时对字符串相加或相减会放到堆中(放之前会先验证方法区中是否含有相同的字符串常量,如果存在,把地址返回,如果不存在,先将字符串常量放到池中,然后再返回该对象的地址)
1、char charAt(int index) 返回 char指定索引处的值。
String str1 = "努力学习!";
char chatStr1 = str1.charAt(4); //!
2、compareTo(String anotherString) 按字典顺序比较两个字符串。相等返回0,大于返回1,小于返回-1。
String s1 = "ab";
String s2 = "ac";
int result1 = s1.compareTo(s2); //-1
3、contains(CharSequence s) 当且仅当此字符串包含指定的char值序列时才返回true。
String str1 = "努力学习!";
String str2 = "学习";
boolean judge1 =str2.contains(str1); //false
boolean judge2 =str1.contains(str2); //true
4、endsWith(String suffix) 测试此字符串是否以指定的后缀结尾。
String str1 = "努力学习!";
boolean judge3 = str1.endsWith("!"); //true
5、equals(Object anObject) 将此字符串与指定对象进行比较。
String s1 = "ab";
String s2 = "ac";
boolean judge4 =s1.equals(s2); //false
6、equalsIgnoreCase(String anotherString) 判断两个字符串是否相等,并且同时忽略大小写。
String s1 = "ab";
String s4 = "AB";
boolean judge5 = s1.equalsIgnoreCase(s4); //true
7、getBytes() 使用平台的默认字符集将此 String编码为字节序列,将结果存储到新的字节数组中。
String str3 = "测试A";
byte[] strByte1 = str3.getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < strByte1.length; i++){
System.out.print(strByte1[i] + ","); //-26,-75,-117,-24,-81,-107,65,
}
8、indexOf(String str) 返回指定子字符串第一次出现的字符串内的索引。
String str4 = "我们要努力学号Java,因为以后靠Java混口饭吃吃。";
int result4 = str4.indexOf("Java"); //7
9、isEmpty() 判断某个字符串是否为“空字符串”。
String s5 = "";
boolean judge6 = s5.isEmpty(); //true
10、lastIndexOf(String str) 返回指定子字符串最后一次出现的字符串中的索引。如不存在返回-1.
String str4 = "我们要努力学号Java,因为以后靠Java混口饭吃吃。";
int result5 = str4.lastIndexOf("Java"); //17
11、replace(CharSequence target, CharSequence replacement) 将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。
String str5 = "一二三四五六七八九十";
String str6 = "十九八七六五四三二一";
String str7 = "数字一二三四五六七八九十数字";
str7 = str7.replace(str5, str6); //数字十九八七六五四三二一数字
12、split(String regex) 将此字符串分割为给定的正则表达式的匹配。
String str8 = "123,456,789";
String stander = "/^[\\w,]+$/"; //正则表达式:以逗号分隔
String[] stringArray = str8.split(stander);
for (int i = 0; i < stringArray.length; i++){
System.out.println(stringArray[i]); //123,456,789
}
13、startsWith(String prefix) 测试此字符串是否以指定的前缀开头。
String str9 = "Hello";
boolean judge7 = str9.startsWith("H"); //true
14、substring(int beginIndex, int endIndex) 返回一个字符串,该字符串是此字符串的子字符串。
String str9 = "Hello";
String str10 = str9.substring(1,3); //el
15、toCharArray() 将此字符串转换为新的字符数组。
String str7 = "十九八七六五四三二一"
char[] strArray = str7.toCharArray(); //十,九,八,七,六,五,四,三,二,一";
16、toLowerCase() 将所有在此字符 String使用默认语言环境的规则,以小写。
String s4 = "AB";
String str11 = s4.toLowerCase(); //ab
System.out.println(str11);
17、toUpperCase() 将所有在此字符 String使用默认语言环境的规则大写。
String str12 = s1.toUpperCase(); //AB
18、trim() 返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。
String str13 = " 前面有很多空格".trim(); //前面有很多空格
19、String中只有一个方法是静态的,不需要new对象,这个方法叫做valueOf。将“非字符串”转换为“字符串”。
int num = 602795356;
String str = String.valueOf(num); //602795356
因为Java中的字符串是不可变的,每一次拼接都会产生新字符串。这样会占用大量的方法区内存,造成内存空间的浪费。
StringBuffer底层实际上是一个byte[]数组。往StringBuffer中放字符串,实际上是放到byte数组当中了,StringBuffer的初始化容量是16。append方法底层在进行追加时,如果byte数组满了,就会自动扩容。
如何优化StringBuffer的性能?在创建StringBuffer的时候尽可能给定一个初始化容量。最好减少底层数组扩容次数。预估计以下,给一个大一些的初始化容量。
Scanner in = new Scanner(System in);
System.out.printf("%8.2f",10000/3.0); //3333.33
会以一个字段宽度打印x:这包括8个字符,另外精度为小数点后2个字符。也就是说,这会打印一个前导的空格和7个字符。
要想读取一个文件,需要构造一个Scanner对象,如下所示:
Scanner in = new Scanner(Path.of(“myfile.text”),StandardCharsets.UTF-8);
如果文件名中包含反斜杠符号,就要记住在每个反斜杠之前再加一个额外的反斜杠转义:”c:\\mydirectory\\myfile.txt”。
要想写入文件,就需要构造一个PrintWriter对象。在构造器中,需要提供文件名和字符编码:
PrintWriter out = new PrintWriter(“myfile.txt”,StandardCharsets.UTF-8);
8种控制语句:2种选择语句:if、switch语句;3种循环语句:for、while、do...while语句;2种转向语句:break、continue语句;1种返回语句:return语句。
switch 语句除了支持int 类型外,还支持 String 类型。switch 语句当中 case 是可以进行合并的。
switch 虽然只能探测 int 类型,但是也可以将byte,short,char 类型放到小括号当中,因为这些类型会自动转换成 int 类型。
for(初始化表达式; 布尔表达式; 更新表达式){
循环体;
}
执行顺序:初始化表达式 --> 布尔表达式(true)--> 循环体 --> 更新表达式。
1.break
break语句默认情况下只能终止离它“最近”的“一层”循环。break语句终止的是内部循环,不影响外部循环的执行。
break还可以用来终止指定的循环,当我们得知多层循环嵌套的时候,可以给每个循环设置标识,标签必须放在希望跳出的最外层循环之前,并且必须紧跟一冒号。例如:
read_data:
while(...){
...;
break read_data;
}
2.continue
continue语句则是用来终止当前本次循环,直接进入下一次循环继续执行。
如果基本的整数和浮点数精度都不能够满足需求,那么可以使用java.math包中两个很有用的类:BigInteger和BigDecimal。这两个类可以处理包含任意长度数字序列的数值。需要使用大数类中的add和multiply方法进行“+”和“*”的运算。
数组是一组数据的集合,数组作为一种引用类型(在堆中分配),数组元素的类型可以是基本类型,也可以是引用该类型,但同一个数组只能是同一种类型。
声明数组:
int[] a; //只声明了变量a,并没有将a初始化为一个真正的数组
声明并初始化数组:
int[] a = new int[100]; //数组长度不要求是常量:new int[n]
在Java中,提供了一种创建数组对象并同时提供初始化值的简写形式:
int[] smallPrimes = {2,3,4,5,};
最后一个值后面允许有逗号,如果你要不断为数组增加值,这会很方便。
声明一个匿名数组:
new int[] {17,19,23,29};
在Java中允许有长度为0的数组:new elementType[0]或new elementType[]{}。注意,长度为0的数组与null并不相同。
创建一个数字数组时,所有元素都初始化为0。boolean数组的元素会初始化为false。对象数组的元素则初始化为一个特殊值null,表示这些元素(还)未存放任何对象。
for(int element : a){System.out.print(a);}
Java中允许将一个数组变量拷贝到另一个数组变量。这时,两个变量将引用同一个数组.。
如果希望将一个数组的所有值拷贝到一个新的数组中去,就要使用Arrays类的copyOf方法。第2个参数是新数组的长度。这个方法通常用来增加数组的大小:
copiedLuckNumbers = Arrays.copyOf(luckyNumbers , 2 * luckyNumbers.length);
如果数组元素是数值型,那么额外的元素将被赋值为0;如果数组元素是布尔型,则将赋值为false。相反,如果长度小于原始数组的长度,则只拷贝前面的值。
还可以使用System类中的arraycopy方法:
arraycopy(拷贝源, 第几个元素开始拷贝, 拷贝目标, 从目标第几个位置开始拷贝, 拷贝原数组长度);
每一个Java应用程序都有一个带String arg[]参数的main方法。这个参数表明main方法将接受一个字符串数组,也就是命令行上指定的参数。
JVM调用main方法的时候,会自动传一个String数组过来。
JVM默认传递过来的这个数组对象的长度默认0。通过测试得出,args不是null。
main方法上面的String[] args数组主要是用来接收用户输入参数的。以空格的形式分隔。
要想对数值型数组进行排序,可以使用Arrays类中的sort方法:
int[] a = {10, 8, 15, 17};
Arrays.sort(a); //[8, 10, 15, 17]
声明一个二维数组:
double[][] balances = new double[行][列]
其中方括号的个数就是数组的维数,另外如果知道数组元素,就可以不调用new,而直接使用简写形式对多维数组进行初始化。例如:
int[][] magicSquare = {{1,2,3},{4,5,6},{7,8,9}};
要想快速地打印一个二维数组的数据元素列表,可以调用:
Arrays.deepToString(a)
;实际开发中用的最多的是一维数组,三维数组几乎不用。
优点:
缺点:随机增删效率较低,数组无法存储大数据量。注意:数组最后一个元素的增删效率不受影响。
以上算法java已经封装好了,直接调用就行。只不过以后面试的时候,可能会有机会碰上。算法实际上在java中不需要精通。
定义/声明方法的语法格式:
[修饰符列表] 返回值类型 方法名(形式参数列表){方法体;}
调用方法的语法格式是(前提是方法的修饰符列表中带有 static 关键字):“类名.方法名(实际参数列表);”。
当在 a()方法执行过程中调用 b()方法的时候,并且 a()方法和 b()方法在同一个类当中,此时“类名.”可以省略不写,但如果 a()方法和 b()方法不在同一个类当中,“类名.”则不能省略。
栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是:仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈(push),它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈、退栈或弹栈(pop),它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
栈数据结构存储数据有这样的特点:先进后出,或者后进先出原则。也就是说最先进去的元素一定是最后出去,最后进去的元素一定是最先出去,因为一端是开口的,另一端是封闭的。栈帧永远指向的是栈顶部的元素。处于栈顶部的元素具备活跃权。
1.JVM的内存结构中三块比较重要的内存空间
上图是一张标准的Java虚拟机内存结构图。目前我们只看其中的“栈”和“方法区”,其它的后期研究,方法区中存储类的信息,或者也可以理解为代码片段,方法在执行过程中需要的内存空间在栈中分配。
java 程序开始执行的时候先通过类加载器子系统找到硬盘上的字节码(class)文件,然后将其加载到 java 虚拟机的方法区当中,开始调用 main 方法,main 方法被调用的瞬间,会给 main 方法在“栈”内存中分配所属的活动空间,此时发生压栈动作,main 方法的活动空间处于栈底。
2.程序运行时的内存变化
方法只定义不去调用的话,只是把它的代码片段存储在方法区当中,java 虚拟机是不会在栈内存当中给该方法分配活动空间的方法只定义不调用是不会执行的。方法调用时:压栈 (在栈中给该方法分配空间)。方法执行结束时:弹栈(将该方法占用的空间释放,局部变量的内存也释放。)例如:
public class MethodTest {
public static void main(String[] args) {
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1() {
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2() {
System.out.println("m2 begin");
System.out.println("m2 over");
}
}
方法重载(overload)是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。调用重载方法时,Java 编译器能通过检查调用的方法的参数类型和个数选择一个恰当的方法。方法重载通常用于创建完成一组任务相似但参数的类型或参数的个数不同的方法。调用方法时通过传递给它们的不同个数和类型的实参来决定具体使用哪个方法。在同一个类当中,如果多个功能是相似的,可以考虑将它们的方法名定义的一致,使用方法重载机制。满足以下三个条件:
方法自身调用自身,这就是方法递归调用。递归在使用的时候必须有结束条件,没有结束条件就会导致无终止的压栈。方法中的代码必须遵循自上而下的顺序依次逐行执行,不能跳行执行,对于栈内存来说一直在进行压栈操作所以没有弹栈操作,总有一天栈内存会不够用的个时候就会出现栈内存溢出错误。