1987WEB视界-分享互联网热点话题和事件

您现在的位置是:首页 > WEB开发 > 正文

WEB开发

Java学习笔记

1987web2024-03-25WEB开发39
编码ASCII:用八位二进制的低七位,一共规定了128个字符的编码,一个字节表示一个字符,扩展ASCII:第八位为1,规定了以1开头的128个字符Unicode:固定

编码 ASCII:用八位二进制的低七位,一共规定了128个字符的编码,一个字节表示一个字符, 扩展ASCII:第八位为1,规定了以1开头的128个字符 Unicode:固定大小的编码,通常两个字节表示一个字符,字母和汉字统一用两个字节,浪费空间 UTF-8:是一种变长的编码方式。字母用一个字节,汉字 ...

编码

ASCII:用八位二进制的低七位,一共规定了128个字符的编码,一个字节表示一个字符,扩展ASCII:第八位为1,规定了以1开头的128个字符Unicode:固定大小的编码,通常两个字节表示一个字符,字母和汉字统一用两个字节,浪费空间UTF-8:是一种变长的编码方式。字母用一个字节,汉字用三个字节,是在互联网上使用最广的一中Unicode的实现方式gbk:可以表示汉字,范围广,字母用一个字节,汉字用两个字节ANSI编码--不同地区采用的编码的统称UTF-8编码规则:1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的Unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode码。编码大小支持语言ASCII1个字节英文Unicode2个字节(生僻字4个)所有语言UTF-81-6个字节,英文字母1个字节,汉字3个字节,生僻字4-6个字节所有语言

基本语法

编写 Java 程序时,应注意以下几点:

大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的。类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如MyFirstJavaClass方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为.java。(如果文件名和类名不相同则会导致编译错误)。主方法入口:所有的 Java 程序由public static void main(String[] args)方法开始执行。

标识符

Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。

关于 Java 标识符,有以下几点需要注意:

所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合关键字和保留字不能用作标识符标识符是大小写敏感的标识符不能包含空格合法标识符举例:age、$salary、_value、__1_value非法标识符举例:123abc、-salary

内置数据类型

Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。

byte:

byte 数据类型是8位、有符号的,以二进制补码表示的整数;最小值是-128(-2^7);最大值是127(2^7-1);默认值是0;byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一;例子:byte a = 100,byte b = -50。

short:

short 数据类型是 16 位、有符号的以二进制补码表示的整数最小值是-32768(-2^15);最大值是32767(2^15 - 1);Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;默认值是0;例子:short s = 1000,short r = -20000。

int:

int 数据类型是32位、有符号的以二进制补码表示的整数;最小值是-2,147,483,648(-2^31);最大值是2,147,483,647(2^31 - 1);一般地整型变量默认为 int 类型;默认值是0;例子:int a = 100000, int b = -200000。

long:

long 数据类型是 64 位、有符号的以二进制补码表示的整数;

最小值是-9,223,372,036,854,775,808(-2^63)

最大值是9,223,372,036,854,775,807(2^63 -1)

这种类型主要使用在需要比较大整数的系统上;

默认值是0L

例子: long a = 100000L,Long b = -200000L。"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。

//注意:java对于整型的默认类型为int,数的末尾要加上Llongn1=100L;

float:

float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;

float 在储存大型浮点数组的时候可节省内存空间;

默认值是0.0f

浮点数不能用来表示精确的值,如货币;

例子:float f1 = 234.5f。

//Java对于浮点数的默认类型为doublefloatf1=1.1f;//末尾的f不可省,否则相当于八个字节的数据放到四个字节,编译会报错

double:

double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数;

浮点数的默认类型为double类型;

double类型同样不能表示精确的值,如货币;

默认值是0.0d

例子:double d1 = 123.4。

System.out.println(8.1/3);//结果为2.6999999999999997//尽量不要直接判断两个浮点数是否相等,应该以两个数的差值的绝对值是否在某个精度范围内来判断两个浮点数是否相等System.out.println(5.2e1);//结果为52.0 注意末尾的0不可以省略

boolean:

boolean数据类型表示一位的信息;

只有两个取值:true 和 false;

这种类型只作为一种标志来记录 true/false 情况;

默认值是false

例子:boolean one = true。

不可以用0/1代替false/true,只有两个值

char:

char类型是一个单一的16 位 Unicode 字符 两个字节;最小值是\u0000(即为0);最大值是\uffff(即为65,535);char 数据类型可以储存任何字符;例子:char letter = A;。

注:字符型的本质,其实是字符对应的ASCII编码

自动类型转换

有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算

intn1=10;floatt1=n1+1.1;//错误,系统会将n1 + 1.1的结果转为默认的double类型,将8字节的double赋给4字节的float会报错floatt2=n1+1.1F;//正确

当把精度大的数据类型赋给精度小的数据类型时,就会报错,反之就会进行自动类型转换

(byte , short ) 和 char 之间不会相互自动类型转换

byteb1=10;//当把数赋给byte时,先判断该数是否在byte范围内[-128,127],在就没问题intn2=1;byteb2=n2;//错误,只有赋的是具体的数时,才会先判断该数是否在byte范围内charc1=b1;//char与byte之间不能相互自动类型转换shorts1=b1;//short与byte之间同样也不能相互自动类型转换

byte , short , char 他们三者之间可以计算,在计算时首先转换为int类型

byteb1=1;byteb2=2;shorts1=b1+b2;//错误,计算后的结果为intcharc1=b1+b2;//错误,原因同上ints2=b1+b2;//correct

Boolean类型不参与自动类型转换

自动提升原则,表达式结果的类型自动提升为操作数中精度最高的类型

强制类型转换

当进行数据精度大小 由大到小的转换时,就需要用到强制类型转换

intn1=(int)1.9;System.out.println("n1="+n1);//n1=1intn2=2000;byteb1=(byte)n2;System.out.println("b1="+b1);//b1=-48 发生溢出

强制类型强转只针对最近的操作数有效,往往会用小括号提高优先级

intx=(int)10*3.5+2.4;//错误,不能将double转换为intintx=(int)(10*3.5+2.4);//正确 37.4->37

char类型可以保存int的常量值,但不能保存int的变量值,需要强转

byte ,short 和 char 类型在进行运算时,当做int类型处理

基本类型与字符串的转换?//基本数据类型转字符串intn1=100;Strings1=n1+"";//+"" 即可将任意基本数据类型转为字符串//字符串转基本类型 使用基本类型对应的包装类的相应方法,得到基本数据类型Strings2="123";intn2=Integer.parseInt(s2);//使用基本数据类型对应的包装类,将String转为int,利用Integer.parseIntbooleanb1=Boolean.parseBoolean("true");//将String转为Boolean类型//注意,如果s2="hello",将其转换为int类型,编译时不会出错,但是执行时会抛出异常Exception导致程序终止//字符串加一个整形数据Strings1="abc";System.out.println(s1+1);// 结果为abc1

字符串的内容比较使用字符串的equal方法

==判断是两个字符串是否在同一块内存空间,而equal判断的是内容是否相等

Stringname=scan.next();System.out.println("SVicen",equals(name));//比较传入的字符串name是否等于SVicenstring[]names={"金毛狮王",“白眉鹰王","紫衫龙王“};for(inti=0;i<names.length;i++){if(name.equals(names[i])){System.out.println("找到了");}}

引用类型

在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。对象、数组都是引用数据类型。所有引用类型的默认值都是null。一个引用变量可以用来引用任何与之兼容的类型。例子:Site site = new Site("Nowcoder")。

常量

常量在程序运行时是不能被修改的。

在 Java 中使用 final 关键字来修饰常量,声明方式和变量类似:

finaldoublePI=3.1415927;

虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。

字面量可以赋给任何内置类型的变量。例如:

bytea=68;chara=A

byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。

当使用字面量的时候,前缀 0 表示 8 进制,而前缀 0x 代表 16 进制, 例如:

intdecimal=100;intoctal=0144;inthexa=0x64;

和其他语言一样,Java的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:

"Hello World""two\nlines""\"This is in quotes\""// \"将"转义为",若不加",会将字符串内容认为前两个双引号的内容,后面的内容报错

字符串常量和字符变量都可以包含任何 Unicode 字符。例如:

chara=\u0001;Stringa="\u0001";

转义字符

符号字符含义\n换行 (0x0a)\r回车 (0x0d)\f换页符(0x0c)\b退格 (0x08)\0空字符 (0x20)\s字符串\t制表符"双引号单引号\反斜杠\ddd八进制字符 (ddd)\uxxxx16进制Unicode字符 (xx

注:\r 回车符的意思并不是换行,而是将光标移动到当前行的开始位置 详见下列

System.out.println("helloworld\r北京");//输出结果为北京oworld 因为一个汉字占两个字节

变量类型

Java语言支持的变量类型有:

类变量:独立于方法之外的变量,用 static 修饰。实例变量:独立于方法之外的变量,不过没有 static 修饰。局部变量:类的方法中的变量。publicclassVariable{staticintallClicks=0;// 类变量Stringstr="hello world";// 实例变量publicvoidmethod(){inti=0;// 局部变量}}

接收用户输入

importjava.util.Scanner;//1.引入Scanner类(简单文本扫描器)所在的包//2.创建Scanner对象ScannermyScanner=newScanner(System.in);System.out.println("请输入名字");Stringname=myScanner.next();//接收用户输入System.out.println("请输入年龄");intage=myScanner.nextInt();System.out.println("名字:"+name+"\n年龄:"+age);myScanner.close();//使用完后应该close掉importjava.util.Scanner;publicclassScannerDemo{publicstaticvoidmain(String[]args){Scannerscan=newScanner(System.in);// 从键盘接收数据// next方式接收字符串System.out.println("next方式接收:");// 判断是否还有输入if(scan.hasNext()){Stringstr1=scan.next();System.out.println("输入的数据为:"+str1);}scan.close();}}

读入char型数据char ch = scan.next().charAt(0); --返回指定索引处的 char 值,这里的索引就是0

char ch = scan.next().charAt(0); --如果输入的是"你好",输出结果为你

next() 与 nextLine() 区别

next():

1、一定要读取到有效字符后才可以结束输入。2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。next() 不能得到带有空格的字符串。

nextLine():

1、以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。2、可以获得空白。

如果要输入 int 或 float 类型的数据,在 Scanner 类中也有支持,但是在输入之前最好先使用 hasNextXxx() 方法进行验证,再使用 nextXxx() 来读取:

流程控制

分支控制

if...else if...else 语句

if 语句后面可以跟 else if…else 语句,这种语句可以检测到多种可能的情况。

使用 if,else if,else 语句的时候,需要注意下面几点:

if 语句至多有 1 个 else 语句,else 语句在所有的 else if 语句之后。if 语句可以有若干个 else if 语句,它们必须在 else 语句之前。一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行。booleanb=true;if(b=false){//注意这里将b赋值为false 此时第一个if的判断语句为false,不会执行System.out.println("a");}elseif(b){System.out.println("b");}elseif(!b){//这里判断为true,会执行,最终输出结果为 cSystem.out.println("c");}else{System.out.println("d");}

switch case 语句

switch(expression){casevalue://语句break;//可选,一般加上casevalue://语句break;default://可选//语句}

switch case 语句有如下规则:

switch 语句中的变量类型可以是: byte、short、int 、enum或者 char。(注意不可以使float或double),从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。

switch case 执行时,一定会先进行匹配,匹配成功返回当前 case 的值,再根据是否有 break,判断是否继续输出,或是跳出判断。

charc=a;switch(c){casea:System.out.println("ok1");break;case65://这样是可以的,字符型实质上就是intSystem.out.println("ok2");break;default:System.out.println("ok3");}循环结构

for循环

关于 for 循环有以下几点说明:

最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。然后,检测布尔表达式的值。如果为 true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。执行一次循环后,更新循环控制变量。再次检测布尔表达式。循环执行上面的过程。

增强 for 循环--Java5中引入

for(声明语句:表达式){//代码句子}

声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

表达式:表达式是要访问的数组名,或者是返回值为数组的方法。

publicclassTest{publicstaticvoidmain(Stringargs[]){int[]numbers={10,20,30,40,50};for(intx:numbers){System.out.print(x);System.out.print(",");}System.out.print("\n");String[]names={"James","Larry","Tom","Lacy"};for(Stringname:names){System.out.print(name);System.out.print(",");}}}//运行结果为//10,20,30,40,50,//James,Larry,Tom,Lacy,

break 关键字

break 主要用在循环语句或者 switch 语句中,用来跳出整个语句块。

break 跳出最里层的循环,并且继续执行该循环下面的语句。

可以配合label使用, break label1; //跳出label层循环

数组

数组中的元素可以使任意数据类型,包括基本类型和引用类型。但是不能混用

数组创建后,如果没有赋值,有默认值int,short,byte,long->0, float,double->0.0, char->\u0000, boolean->false,String->null

数组属于引用类型,数组型数据是对象

数组的赋值为引用传递,传递的是一个地址

int[]arr1={1,2,3,4};int[]arr2=arr1;arr2[0]=5;//修改arr2的值,arr1的值也会发生改变

声明数组变量

dataType[]arrayRefVar;// 首选的方法dataType arrayRefVar[];// 效果相同,但不是首选方法

创建数组

Java语言使用new操作符来创建数组,语法如下:

arrayRefVar=newdataType[arraySize];

上面的语法语句做了两件事:

使用 dataType[arraySize] 创建了一个数组。把新创建的数组的引用赋值给变量 arrayRefVar。

数组变量的声明,和创建数组可以用一条语句完成,如下所示:

dataType[]arrayRefVar=newdataType[arraySize];

还可以使用如下的方式创建数组(静态初始化):

dataType[]arrayRefVar={value0,value1,...,valuek};

数组扩容

int[]arr1={1,2,3};//要在arr1后加一个元素,注意不可以直接arr[3] = 4; --发生数组下标越界int[]newArr=newint[arr1.length+1];for(inti=0;i<arr1.length;i++){newArr[i]=arr1[i];}newArr[newArr.length-1]=4;arr1=newArr;//将扩容后的数组赋给原来的数组For-Each 循环

又叫加强型循环,能在不使用下标的情况下遍历数组。

publicclassTestArray{publicstaticvoidmain(String[]args){double[]myList={1.9,2.9,3.4,3.5};//打印所有数组元素for(doubleelement:myList){System.out.println(element);}}}多维数组

多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组,例如:

Stringstr[][]=newString[3][4];int[][]y或者inty[][]或者int[]y[];int[][]a={{1,2,3},{4,5,6},{7,8,9}};for(inti=0;i<3;i++){for(intj=0;j<3;j++){System.out.print(a[i][j]+" ");}System.out.println("");}System.out.println(a[0][2]);//注意java语言 这里不可以输出 a[0][3] 而c++可以,输出的为第二行第一列的4//由此可见c++的多维数组内存存储空间是连续的,而java的多维数组内存空间是不连续的

Java的多维数组的每个数组的数据个数可以不同,因为每个一维的数组可以单独new一定的空间

多维数组的动态初始化(以二维数组为例)

1.直接为每一维分配空间,格式如下:

type[][]typeName=newtype[typeLength1][typeLength2];例如inta[][]=newint[2][3];int[][]arr={{1},{1,2},{1,2,3}};

2.从最高维开始,分别为每一维分配空间,例如:

String[]strs=newString[]{"a","b","c"};//是正确的 strs其实是一维的,只不过直接静态赋值了//注意这里String[] strs = new String[3] { "a", "b", "c" } 就是错误的Strings[][]=newString[2][];s[0]=newString[2];//每一个一维的数组都需要 使用new运算符,否则其为空-无法存放数据s[1]=newString[3];//注意这里 s[0]中两个元素,而s[1]中有三个元素s[0][0]=newString("Good");s[0][1]=newString("Luck");s[1][0]=newString("to");s[1][1]=newString("you");s[1][2]=newString("!");

s[0]=new String[2]s[1]=new String[3]是为最高维分配引用空间,也就是为最高维限制其能保存数据的最长的长度,然后再为其每个数组元素单独分配空间s0=new String("Good")等操作。

Arrays 类

ava.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。

具有以下功能:

给数组赋值:通过 fill 方法。对数组排序:通过 sort 方法,按升序。比较数组:通过 equals 方法比较数组中元素值是否相等。查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。序号方法和说明1public static int binarySearch(Object[] a, Object key)用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。2public static boolean equals(long[] a, long[] a2)如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。3public static void fill(int[] a, int val)将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。4public static void sort(Object[] a)对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。

JAVA内存结构

栈:一般存放基本数据类型(局部变量)和类的方法堆:存放引用型数据,对象(对象的各个属性的地址)、数组(通过new创建的数据类型)方法区:常量池(存放常量比如字符串),类加载信息(具体对象的各个属性的值)

类与对象

一个类可以包含以下类型变量:

局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。成员变量:又叫全局变量,全局变量又分为类变量(静态变量)、实例变量两种。成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。类变量:也称为静态变量,类变量也声明在类中,方法体之外,但必须声明为 static 类型。局部变量局部变量声明在方法、构造方法或者语句块中;局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁;访问修饰符不能用于局部变量;局部变量只在声明它的方法、构造方法或者语句块中可见;局部变量是在栈上分配的局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。实例变量?实例变量声明在一个类中,但在方法、构造方法和语句块之外,没有 static 修饰;当一个对象被实例化之后,每个实例变量的值就跟着确定;实例变量在对象创建的时候创建,在对象被销毁的时候销毁;实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息;实例变量可以声明在使用前或者使用后;访问修饰符可以修饰实例变量;实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:ObejectReference.VariableName。类变量(静态变量)类变量也称为静态变量,在类中以 static 关键字声明,但必须在方法之外。无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。静态变量除了被声明为常量外很少使用。常量是指声明为public/private,final和static类型的变量。常量初始化后不可改变。静态变量储存在静态存储区。经常被声明为常量,很少单独使用static声明变量。随着类的加载而存在,随着类的消失而消失。静态变量在第一次被访问时创建,在程序结束时销毁。与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为public类型。默认值和实例变量相似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。静态变量可以通过:ClassName.VariableName的方式访问。类变量被声明为public static final类型时,类变量名称一般建议使用大写字母。如果静态变量不是public和final类型,其命名方式与实例变量以及局部变量的命名方式一致。

?属性和局部变量可以重名,访问时遵循就近原则

?属性随着对象的创建而创建,随着对象的销毁而销毁,其实非静态属性就是实例变量

?全局变量/属性:可以被本类使用,也可以被其他类使用,创建对象的时候实例化

?全局变量/属性可以加修饰符,但局部变量不可以加修饰符

构造方法/构造器

构造器是对对象进行初始化的而不是创建对象的

每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法进行对象的初始化。

在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,且没有返回值。也不写void。

一旦定义了自己的构造器,系统默认的构造器就被覆盖了,不能使用,除非显示的再定义一下默认无参构造器

下面是一个构造方法示例:

publicclassPuppy{publicPuppy(){}publicPuppy(Stringname){// 这个构造器仅有一个参数:name}}

反汇编指令javap 类名(.class后缀) 可以加 -c -v 选项

this

-- 与对象关联,实际上每个对象在堆区分配空间的时候就隐式分配了一个this指向它自己

使用this输出的值一定是属性值,但是如果不加this,如果有局部变量与属性同名,则会就近输出局部变量

publicstaticvoidmain(String[]args){T t1=newT();}classT{publicT(){//对this的调用必须在构造器的第一条语句//访问构造器语法:this(参数列表) 注意只能在一个构造器中访问另一个构造器 且this语句必须放在第一条this("SVicen",20);//在一个构造器内调用其他的构造器System.out.println("T()构造器调用");}publicT(Stringname,intage){System.out.println("T(String name, int age)构造器调用");}}//输出结果如下T(Stringname,intage)构造器调用T()构造器调用

IDEA自定义模板 settings-> editor -> live templates

创建对象

对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:

声明:声明一个对象,包括对象名称和对象类型。实例化:使用关键字 new 来创建一个对象。初始化:使用 new 创建对象时,会调用构造方法初始化对象。publicclassPuppy{intage=10;Stringname;publicPuppy(Stringn,inta){//构造函数name=n;age=a;//这个构造器仅有一个参数:nameSystem.out.println("小狗的名字是 : "+name);}publicstaticvoidmain(String[]args){// 下面的语句将创建一个Puppy对象PuppymyPuppy=newPuppy("tommy",18);}}//结果为:小狗的名字是 : tommy匿名对象只可以使用一次publicclassCircle{doubleradius;//半径publicCircle(doubler){radius=r;}publicdoublearea(){returnMath.PI*radius*radius;}publicdoublelen(){return2*Math.PI*radius;}publicdoubletest(){doublea=1.0;System.out.println(2*a);}publicstaticvoidmain(String[]args){newCircle.test();}}对象创建流程详解(以上代码为例)?Puppy myPuppy = new Puppy( "tommy" )后 先在方法区加载类的属性和方法信息 ---只会加载一次在堆开辟空间(根据对象的属性个数)对对象进行默认初始化(Sting型为null,int型为0,boolean为false,char型为空),age初始化为0,name初始化为null进行类的定义中对属性的显式初始化,age = 10;name = null进行构造器的初始化,将传入的参数赋值给创建的对象的属性对应的堆空间将创建的对象的地址返回给创建的对象myPuppy (对象的引用)对象的赋值publicclassObject{publicstaticvoidmain(String[]args){//创建Person对象Personp1=newPerson(20,,"SVicen");System.out.println("姓名为:"+p1.name+" 性别为:"+p1.gender+" 年龄为:"+p1.age);Personp2=p1;//注意这里传值,传的其实是引用,修改p1,p2任意一个的值都会导致两个都发生变化p2.age=18;//但是如果令p2=null,只是将p2指向的地址改为了null,并不影响p1的值p1.gender=;System.out.println("=====第一个人的属性如下=====");System.out.println("姓名为:"+p1.name+" 性别为:"+p1.gender+" 年龄为:"+p1.age);System.out.println("=====第二个人的属性如下=====");System.out.println("姓名为:"+p2.name+" 性别为:"+p2.gender+" 年龄为:"+p2.age);}}classPerson{intage;chargender;Stringname;Person(inta,charg,Strings){this.age=a;this.gender=g;this.name=s;}}//输出结果如下姓名为:SVicen性别为:男年龄为:20=====第一个人的属性如下=====姓名为:SVicen性别为:女年龄为:18=====第二个人的属性如下=====姓名为:SVicen性别为:女年龄为:18源文件声明规则

在本节的最后部分,我们将学习源文件的声明规则。当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则。

一个源文件中只能有一个public类一个源文件可以有多个非public类源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。如果一个类定义在某个包中,那么package语句应该在源文件的首行。如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

类有若干种访问级别,并且类也分不同的类型:抽象类和final类等。这些将在访问控制章节介绍。

除了上面提到的几种类型,Java还有一些特殊的类,如:内部类、匿名类。

Java包

包主要用来对类和接口进行分类。当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。

IDEA,新建packet,名字为com.xiaoming -- 会创建二级目录xiaoming,以及目录com

包的命名一般为:com.公司名.项目名.业务模块名

包的作用

1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。2、区分相同名字的类,同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类

Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。

包语句的语法格式为:

//例如 它的路径应该是 net/java/util/Something.java 这样保存的packagenet.java.util;// 一个类中只能有一个packegpublicclassSomething{...}

一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。

开发者可以自己把一组类和接口等打包,并定义自己的包。而且在实际开发中这样做是值得提倡的,当你自己完成类的实现之后,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的。

由于包创建了新的命名空间(namespace),所以不会跟其他包中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。引用时只可以引入一个包,否则编译器无法区分。

以下是一些 Java 中的包:

java.lang-----打包基础的类,默认引入java.io-----包含输入输出功能的函数java.util.*-----util包,系统提供的工具包,工具类,使用Scannerjava.net.*-----网络包,网络开发java.awt.*----做java的界面开发,GUI创建包

创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。

包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。

如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。

在 animals 包中加入一个接口(interface):

/* 文件名: Animal.java */packageanimals;interfaceAnimal{publicvoideat();publicvoidtravel();}

接下来,在同一个包中加入该接口的实现:

packageanimals;/* 文件名 : MammalInt.java */publicclassMammalIntimplementsAnimal{publicvoideat(){System.out.println("Mammal eats");}publicvoidtravel(){System.out.println("Mammal travels");}publicintnoOfLegs(){return0;}publicstaticvoidmain(Stringargs[]){MammalIntm=newMammalInt();m.eat();m.travel();}}可变参数?

注:当有多个参数时,可变参数需在最后一个参数的位置,否则无法确定个数,同时一个函数无法有多个类型的可变参数

classMethod{publicintsum(int...nums){intres=0;for(inti=0;i<nums.length;i++){res+=nums[i];}returnres;}publicvoidf1(double...nums,doubled1){//这样写是错误的}//使用可变参数时,可以当做数组使用,即nums当做数组Methodm=newMethod();System.out.println(m.sum(5,10,100));System.out.println(m.sum(5,10,20,50,100));//可以输入任意参数个数}Java 修饰符

Java语言提供了很多修饰符,主要分为以下两类:

访问修饰符非访问修饰符

修饰符用来定义类、方法或者变量,通常放在语句的最前端。

访问控制修饰符?

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

default(即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。private: 在同一类内可见。使用对象:变量、方法。注意:不能修饰类(外部类)public: 对所有类可见。使用对象:类、接口、变量、方法protected: 对同一包内的类和所有子类可见。使用对象:变量、方法。注意:不能修饰类(外部类)。修饰符当前类同一包内子孙类(同一包)子孙类(不同包)其他包publicYYYYYprotectedYYYY/NNdefaultYYYNNprivateYNNNN封装

在面向对象程式设计方法中,封装(Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

封装的优点良好的封装能够减少耦合。 高内聚,低耦合类内部的结构可以自由修改。可以对成员变量进行更精确的控制,保证数据安全合理隐藏实现细节。封装实现步骤属性私有化提供一个公共的public set方法,用于对属性判断并赋值提供一个公共的public get方法,用于获取属性的值继承继承的概念

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

类的继承格式

在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:

class父类{}class子类extends父类{}继承的构造器调用?

当创建子类对象时,不管是用子类的哪个构造器,默认情况下都会去调用父类的无参构造器(通过一个super()函数 ),如果父类没有提供无参构造器(定义了有参构造器而没有对无参构造器进行显示的声明),则必须**在子类的构造器中显示用super(构造器形参列表) 来指明调用父类的有参构造器来完成父类的初始化。 **

super()与this()类似,都是只能应用在构造器里,且必须放在构造器的第一行。但可以在普通非静态方法中通过super.eat()调用父类的非静态方法。

继承类型

Java 不支持多继承,但支持多重继承。

继承的特性子类拥有父类非 private 的属性、方法。子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。子类可以用自己的方式实现父类的方法。Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。继承关键字

继承可以使用extendsimplements这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在java.lang包中,所以不需要import)祖先类。

extends关键字

在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。

publicclassAnimal{privateStringname;privateintid;publicAnimal(StringmyName,Stringmyid){//初始化属性值}publicvoideat(){//吃东西方法的具体实现 }publicvoidsleep(){//睡觉方法的具体实现 }}publicclassPenguinextendsAnimal{}implements关键字

使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。

publicinterfaceA{publicvoideat();publicvoidsleep();}publicinterfaceB{publicvoidshow();}publicclassCimplementsA,B{}super关键字?

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super取访问爷爷类的成员(前提是直接父类中没有同名成员),如果多个上级类中都有同名的成员,使用super访问遵循就近原则,

classAnimal{voideat(){System.out.println("animal : eat");}}classDogextendsAnimal{voideat(){System.out.println("dog : eat");}voideatTest(){this.eat();// this 调用自己的方法super.eat();// super 调用父类方法}}final关键字

final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:

继承的内存布局?

创建son对象时,father和grandpa对象都会进行构造且放在一块内存,对于father或grandpa的private属性son是不可以访问的

son s=newson();//通过son访问名字时,如果子类有该属性,则访问子类的,否则访问父类的,知道Object类(最终父类)s.name;//大头儿子s.age;//爸爸的年龄 39s.hobby;//爷爷的爱好 旅游publicclassExtendExercise{publicstaticvoidmain(String[]args){B b=newB();//创建子类对象}}classA{A(){System.out.println("a");}A(Stringname){System.out.println("a name");}}classBextendsA{B(){//调用自己的有参构造Rthis("abc");System.out.println("b");}B(Stringname){//默认有一个 superSystem.out.println("b name");}}//输出结果为ab nameb重写(Override)

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。

classAnimal{publicvoidmove(){System.out.println("动物可以移动");}}classDogextendsAnimal{publicvoidmove(){System.out.println("狗可以跑和走");}}publicclassTestDog{publicstaticvoidmain(Stringargs[]){Animala=newAnimal();// Animal 对象Animalb=newDog();// Dog 对象a.move();// 执行 Animal 类的方法 动物可以移动b.move();//执行 Dog 类的方法 狗可以跑和走}}

在上面的例子中可以看到,尽管b属于Animal类型,但是它运行的是Dog类的move方法。

这是由于在编译阶段,只是检查参数的引用类型(最前面声明的类型)。

然而在运行时,Java虚拟机(JVM)指定对象的类型并且运行该对象的方法。

因此在上面的例子中,之所以能编译成功,是因为Animal类中存在move方法,然而运行时,运行的是特定对象的方法。

classAnimal{publicvoidmove(){System.out.println("动物可以移动");}}classDogextendsAnimal{publicvoidmove(){System.out.println("狗可以跑和走");}publicvoidbark(){System.out.println("狗可以吠叫");}}publicclassTestDog{publicstaticvoidmain(Stringargs[]){Animala=newAnimal();// Animal 对象Animalb=newDog();// Dog 对象a.move();// 执行 Animal 类的方法b.move();//执行 Dog 类的方法b.bark();}}

这里,该程序将抛出一个编译错误,因为b的引用类型Animal(最左边的声明类型) 没有bark方法,编译不成功。

方法的重写规则?参数列表必须完全与被重写方法的相同。返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。 比如说子类返回String(引用型),父类返回Object访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。 默认的权限比protected低,比private高。父类的成员方法只能被它的子类重写。声明为 final 的方法不能被重写。声明为 static 的方法不能被重写,但是能够被再次声明。子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。构造方法不能被重写。如果不能继承一个方法,则不能重写这个方法。重写与重载之间的区别区别点重载方法重写方法参数列表必须修改一定不能修改返回类型可以修改一定不能修改发生范围本类父子类异常可以修改可以减少或删除,一定不能抛出新的或者更广的异常访问修饰符可以修改一定不能做更严格的限制(可以降低限制)多态?

多态:方法或对象具有多种形态,是OOP的第三大特征,建立在封装和继承基础之上。

多态的优点消除类型之间的耦合关系可替换性可扩充性接口性灵活性简化性可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。多态存在的三个必要条件继承重写父类引用指向子类对象对象的多态一个对象的编译类型和运行类型可以不一致编译类型在定义多态时就确定了,不能改变,而运行类型是可以改变的(类似于c++的父类指针指向子类对象)编译类型看定义时 =号 的左边,运行类型看 =号 的右边,可以通过Object的getClass方法查看运行类型多态的向上转型?

父类的引用指向了子类对象(子类的对象向上转型)

?当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

写代码时看的是编译类型,可以调用编译类型的所有可访问的成员,不能调用子类特有的成员

?写代码时能调用的方法和属性都是编译类型的,但运行时调用的方法时从子类开始找的

调用方法时,从子类(运行类型)向上查找方法调用,(有可能父类定义的函数子类未定义 --但要注意这就不是多态了)

?调用方法时,体现多态--其实就是子类对父类的方法做了具体的实现,所以应该从子类开始找方法。

修改后的父类可以调用父类的所有成员(访问权限满足),但是不能调用子类特有的成员(父类中必须有对应的接口)?

Animalani=newAnimal();// 编译类型为Animal 运行类型为Animalani=newDog();//父类引用指向Dog子类 编译类型为Animal 运行类型为 Dogani=newCat();//父类引用指向Cat子类 编译类型为Animal 运行类型为 Catani.eat();//注意这里先看子类的方法,运行时关注运行类型,输出的是猫吃鱼,如果子类没有eat方法再去调用父类的publicclassTest{publicstaticvoidmain(String[]args){show(newCat());// 以 Cat 对象调用 show 方法show(newDog());// 以 Dog 对象调用 show 方法Animala=newCat();// 向上转型a.eat();// 调用的是 Cat 的 eatCatc=(Cat)a;// 向下转型c.work();// 调用的是 Cat 的 work}publicstaticvoidshow(Animala){a.eat();// 类型判断if(ainstanceofCat){// 猫做的事情 instanceof用于判断对象的运行类型是否为Cat类型或Cat类型的子类型Catc=(Cat)a;c.work();}elseif(ainstanceofDog){//狗做的事情 instanceof用于判断对象的运行类型是否为Dog类型或Dog类型的子类型Dogc=(Dog)a;c.work();}}}abstractclassAnimal{abstractvoideat();//abstract 抽象类}classCatextendsAnimal{publicvoideat(){System.out.println("吃鱼");}publicvoidwork(){System.out.println("抓老鼠");}}classDogextendsAnimal{publicvoideat(){System.out.println("吃骨头");}publicvoidwork(){System.out.println("看家");}}//输出结果吃鱼抓老鼠吃骨头看家吃鱼抓老鼠多态的向下转型

其实就是把执行子类对象的父类引用,转为执行子类对象的子类引用

语法:子类类型 引用名 = (子类类型) 父类引用Cat c = (Cat) animal只能强转父类的引用,不能强转父类对象 ?要求父类的引用必须指向当前要转型后的类型的对象,即上面的animal必须指向cat子类对象 ?当向下转型后,就可以调用子类型中的所有成员

属性没有重写之说, --看编译类型

publicclassTest{publicstaticvoidmain(String[]args){Basebase=newSub();//向上转型System.out.println(base.count);//看编译类型,为Base,结果为10Subsub=newSub();System.out.println(sub.count);//编译类型为Sub, 结果为20}}classBase{intcount=10;}classSub{intcount=20;}

instanceOf 比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型。

动态绑定机制DynamicBinding当调用对象方法时,该方法会和该对象的内存地址(运行类型)绑定当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用

?详情见com.poly.dynamic 例子以及多态数组的实现

多态数组内会用到 instanceof 操作符

多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型 详见com\poly\polyexercise\TestPolyParameter.java

Object类(顶级父类)详解?

是类层次结构的根类,在java.lang.Object包里,每个类都是要它作为超类,所有对象(包括数组)都实现这个类的方法

Object类的equals()方法:指示其他某个对象是否与此对象“相等,只能比较引用类型。

publicbooleanequals(Objectobj){return(this==obj);}//可见默认的Object的equals方法直接 用==判断,判断的是地址是否相等。子类往往需要重写

可以判断基本数据类型,判断的是值是否相等,也可以判断引用数据类型,判断的是地址是否相等,即是否是一个对象

子类没有重写 Object 类中的 equals 方法,equals方法和==号比较引用数据类型无区别,重写后的equals方法比较的是对象中的属性。

String类的equals方法,重写了Object的equals方法,判断的是两个字符串的内容是否相等,每个字符逐一比较

publicbooleanequals(ObjectanObject){if(this==anObject){returntrue;}if(anObjectinstanceofString){StringaString=(String)anObject;if(coder()==aString.coder()){returnisLatin1()?StringLatin1.equals(value,aString.value)//拉丁语和UTF16分开处理:StringUTF16.equals(value,aString.value);}}returnfalse;}

Object类的hashCode()方法:返回该对象的哈希码值。默认情况下,该方法会根据对象的地址来计算。(java是在虚拟机jvm跑的,所以一般没有运行地址一说,这里的地址只是参考概念)

不同对象的 hashCode() 的值一般是不相同。但是,同一个对象的hashCode() 值肯定相同,两个引用指向一个对象值也相同。

Object类的toString()方法:返回该对象的字符串表示。包名.类名@hashCode 一般会重写为输出内容。可以使用快捷键创建重写。

publicStringtoString(){returngetClass().getName()+"@"+Integer.toHexString(hashCode());}//getClass().getName() 类的包名+类名//toHexString(hashCode()) 将对象的hashcode值转换成16进制字符串//重写toString方法@OverridepublicStringtoString(){return"Person{"+"name="+name+\+", age="+age+", gender="+gender+};}

当输出一个对象时System.out.println(person);会默认调用 person.toString 方法

Object类的finalize()方法,finalize 是一个 protected 的方法。虽然不是 public 的,但是这个方法也同样能够被所有子类继承

当对象被回收时,系统会自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作

什么时候被回收:当某个对象没有任何引用时,则jvm就会认为这个对象是个垃圾,垃圾回收器就会回收(销毁)对象,在销毁对象前,会调用该对象的finalize方法,程序员可以再该方法中写业务逻辑代码,比如释放资源,数据库连接,文件资源释放等。

其基本原理是:如果垃圾回收器准备对某对象占用的内存资源进行回收,会先将该对象放入回收队列,处于回收队列中的对象会执行其finalize()方法,做一些清除前的工作,例如资源释放等;直至下一次垃圾回收动作发生时才会真正回收对象占用的内存空间。但这并不意味着我们在编程时把应该finalize()方法作为类似于“析构函数”使用。因为该方法只会在垃圾回收时才会执行,而对象可能是不被垃圾回收的。

jdk9之后以弃用该方法,因为finalize()方法不能保证执行。实际开发中也不会使用。转而使用System.gc()显式调用垃圾收集器。调用这个方法,就相当于通知 JVM,程序员希望能够进行垃圾回收。

断点调试

F7 --跳入方法内 F8 --逐行执行代码 F9 --resume执行到下一个断点 shift + F8 --跳出方法

设置填入源代码 settings->Build,Execution->Debugger->Data Views->Stepping->将java.*和javax.*前的对号取消掉

类与对象进阶

类变量(静态变量)

Static修饰的成员变量,被同一个类的所有对象共享,在类加载的时候就生成了

内存分布:JDK8之前,类变量主要存储在方法区中,JDK8以后,在加载类信息的时候通过反馈机制会在堆中生成相应的class对象

定义语法

访问修饰符 static 数据类型 变量名[推荐]static 访问修饰符 数据类型 变量名

访问类变量

类名.类变量名 [推荐]对象.类变量名 [不推荐],IDEA中无语法提示

类变量的访问也需要遵守相应的访问权限,定义为private则不能访问

类变量的生命周期随着类的创建而创建,随着类的消亡而销毁。与具体的对象无关。

类方法(静态方法)

定义语法

访问修饰符 static 返回数据类型 方法名(){} [推荐]static 访问修饰符 返回数据类型 方法名(){}

调用方法 --也需要满足相应的访问权限

类名.类方法名 [推荐]对象名.类方法名

什么时候需要用到类方法

当方法内不涉及到任何与对象相关的成员时,可以把方法设置为类方法(静态方法),可以不创建实例调用方法,提高开发效率

比如JAVA的Math类中很多方法都是静态方法,所以可以直接Math.sqrt(9)

类方法只能访问类成员,不能使用this和super

普通成员方法既可以访问非静态成员也可以访问静态成员

main方法

main方法形式 public static void main(String[] args) {}

main方法是JAVA虚拟机调用的,所以该方法的访问权限必须是public

JAVA虚拟机在调用方法的时候不需要创建对象,所以必须加上关键字 static

main方法接收String类型的数组参数,该数组中保存执行 java 命令时传递给所运行的类的参数

如下代码编译时 java hello vicen 参数名2 参数名3 结果会输出 第1各参数=vicen, ... ...

publicclasshello(){publicstaticvoidmain(String[]args){for(inti=0;i<args.length;i++){System.out.println("第"+(i+1)"个参数="+args[i]);}}}

由于main方法也是静态方法,也不可以调用本类的非静态成员,要访问本类的非静态成员需要先创建对象再调用。?

代码块?

又称为初始化块,属于类中的成员,类似于方法,用{}包起来但与方法不同,没有方法名、参数、返回值,不用通过对象显示调用。而是加载类时或创建对象时隐式调用。修饰符可选,要写的话也只能写static {}; --分号可以写也可以不写。

好处

相当于另一种形式的构造器(对构造器的补充),可以在调用任何形式的构造器时都执行代码块内的初始化操作。应用场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码重用性

类什么时候被加载?

创建对象实例时(new)创建子类对象实例,父类也会被加载使用类的静态成员时(静态方法,静态属性),使用子类的静态成员父类也会被加载publicclassCodeBlockDetails{publicstaticvoidmain(String[]args){System.out.println(B.bb);//调用子类的静态成员属性时,父类的静态代码块也会执行System.out.println(A.aa);//由于上面调用子类的时候已经调用了父类的静态代码块,这里A的静态代码块不会被调用A a=newA();B b=newB();}}classA{publicstaticintaa=10;//没有实例化对象,调用类的静态成员时以下非静态代码块不会被加载 只有使用new创建实例对象,以下代码块才会执行{System.out.println("A的非静态代码块调用--");}static{System.out.println("A的静态代码块被调用~~");}}classBextendsA{publicstaticintbb=20;{System.out.println("B的非静态代码块调用--");}static{System.out.println("B的静态代码块被调用~~");}}

static 代码块只会被调用一次

普通代码块与类的加载无关系,只有在用new实例化对象时普通代码块才会被调用,并且可以被调用多次(与构造器相似)

publicclassCodeBlockDetails{publicstaticvoidmain(String[]args){//System.out.println(B.bb); //调用子类的静态成员属性时,父类的静态代码块也会执行//System.out.println(A.aa);A a=newA();B b=newB();}}classA{publicstaticintaa=10;//没有实例化对象,调用类的静态成员时一下非静态代码块不会被加载{System.out.println("A的非静态代码块调用--");}static{System.out.println("A的静态代码块被调用~~");}}classBextendsA{publicstaticintbb=20;{System.out.println("B的非静态代码块调用--");}static{System.out.println("B的静态代码块被调用~~");}}//运行结果A的静态代码块被调用~~//先加载类信息,后创建实例化对象 所以先调用静态代码块A的非静态代码块调用--B的静态代码块被调用~~A的非静态代码块调用--//实例化B的对象时,会加载A的类信息,但A的静态代码块已经加载过了,所以只会调用A的非静态代码块B的非静态代码块调用--//如果把上面的实例化对象的两条语句交换位置B b=newB();A a=newA();//输出结果为A的静态代码块被调用~~//加载B的类信息时先加载了A的类信息B的静态代码块被调用~~A的非静态代码块调用--//创建B的实例化对象时右先加载了B的类信息,由于static代码块已调用,所以只调用A的非静态代码块B的非静态代码块调用--A的非静态代码块调用--

创建一个对象时,在一个类调用顺序是

调用静态代码块和静态属性初始化(静态代码块和静态属性初始化调用的优先级一样,如果有多个按定义的先后顺序调用)调用普通代码块和普通属性的初始化(普通代码块和普通属性优先级一样,如果有多个同样按定义的先后顺序调用)最后调用构造器(不准确,其实是执行构造器的输出语句,上面的第二步其实是在构造器内隐式调用的)

构造器调用时隐藏了默认调用了自己的普通代码块和普通属性初始化 (public int n = getVal(); 会去调用getVal()方法)

publicclassCodeBlockDetails02{publicstaticvoidmain(String[]args){BB bb=newBB();}}classAA{{System.out.println("AA的普通代码块调用");}publicAA(){System.out.println("AA()的构造器调用");}}classBBextendsAA{{System.out.println("BB的普通代码块调用");}publicBB(){//这里隐含调用了BB的普通代码块,注意与非静态代码块不同,普通代码块在加载类信息时并不会被调用System.out.println("BB()的构造器调用");}}//结果AA的普通代码块调用//实例化BB对象时先调用父类的构造器,父类的构造器又隐式调用了父类自己的普通代码块AA()的构造器调用//调用完父类的普通代码块后才执行父类的构造器BB的普通代码块调用//执行完父类的所有要执行的后再回到子类,执行顺序与父类相同BB()的构造器调用单例设计模式

设计模式:

静态方法和属性的经典使用是在大量的实践中总结和理论化后的优选的代码结构、编程风格以及解决问题的思考方式。就像是经典的棋谱,针对不同的棋局可以使用不同的棋谱

单例设计模式:

采取一定的方法保证在整个的软件系统中对某个类只能存在一个一个对象实例,并且该类只提供一个取得该对象实例的方法

单例模式有两种方式

饿汉式:还没有用到对象的时候在类内已经把对象创建好了,可能造成资源的浪费懒汉式:在使用对象时才会创建实例,存在线程安全的问题

饿汉式实现步骤

构造器私有化 --防止用户直接new类的内部创建一个静态对象并初始化(为了在静态的公共方法中可以放回该对象,故将其创建为静态对象)向外暴露一个静态的公共方法。 getInstance (为了在不创建时就可以调用方法)

懒汉式实现步骤

构造器私有化 --防止用户直接new

定义一个static静态属性对象,但不直接利用new对它进行初始化

提供一个public的static方法,判断如果当前类内对象为null则返回一个new的对象

注意:懒汉式会存在线程安全的问题,多个线程同时调用getInstance方法,有可能创建出多个对象

classCat{privateStringname;privatestaticCatcat;privateCat(Stringname){this.name=name;}publicstaticCatgetInstance(){if(cat==null){//如果还没有创建Cat对象再创建cat=newCat("小猫");}returncat;}Final关键字

final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:

不希望类的某个属性被修改,也可以用final修饰该属性 public final double TAX_RATE = 0.1;

不希望某个局部变量被修改,也可以用final修饰,final int NUM = 1.0; (这时NUM可以成为局部常量)

被final修饰的基本类型变量(四类八种) 不可变!

被final修饰的 引用类型变量 地址不可变!,内容可变!

finalchar[]c={j,a,v,a};System.out.println(c);c[0]=h;//这里是可以修改的System.out.println(c);

final修饰的属性又叫常量,一般用XX_XX命名,注意要大写

final修饰的属性在初始化时必须赋初值,可以在定义属性时,或者在代码块中,或者在构造器中

classA{publicfinaldoubleTAX_RATE=0.1;//在定义时直接赋初值publicfinaldoubleTAX_RATE2;publicfinaldoubleTAX_RATE3;publicA(){//构造器中对常量赋初值TAX_RATE2=0.2;}{//代码块中对常量赋初值TAX_RATE3=0.3;}}

final也可以修饰形参,被修饰后该形参不能被改变(相当于const)

一般一个类如果已经是final类了,就不需要对它下面的方法定义为final方法了

final不能修饰构造器

final和static往往搭配使用,效率更高,用他们修饰的属性时不会导致类加载,底层编译器做了优化。顺序可以颠倒。

publicclassFlnalExer{publicstaticvoidmain(String[]args){System.out.println(B.num);}}classB{//final 和 static 往往搭配使用 效率更高,不会导致类加载,底层编译器做了优化。publicfinalstaticintnum=1000;static{System.out.println("B的静态代码块被调用");}}//加上final后最终输出结果只有 1000//去掉final 输出会有 B的静态代码块被调用 1000

包装类String,Double,Integer,Float,Boolean,Byte,Short,Character都是final类型的,不可以继承

抽象类

如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。

当一个类中含有抽象(abstract)方法时,需要将这个类声明为抽象(abstract)类,类内方法不能有 {}抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。abstract只能修饰类和方法,不能修饰属性和其他的东西抽象方法不能使用private,final和static来修饰,因为这些与重写相违背。static关键字和重写无关抽象类配合模板使用实现模板设计模式。见实例com.svicen.abstract_接口

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

接口与类相似点:接口和类的修饰符都只能是 public 或者 默认

一个接口可以有多个方法。

接口文件保存在 .java 结尾的文件中,文件名使用接口名。

接口的字节码文件保存在 .class 结尾的文件中。

接口相应的字节码文件必须在与包名称相匹配的目录结构中。

接口与类的区别:接口不能用于实例化对象。接口没有构造方法。接口中所有的方法必须是抽象方法。所有方法都是默认public修饰的接口不能包含成员变量,除了 static 和 final 变量。接口不是被类继承了,而是要被类实现。接口支持多继承。使用 implements 关键字接口特性接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为public abstract(只能是 public abstract,其他修饰符都会报错)。接口中可以含有变量,但是接口中的变量会被隐式的指定为public static final变量(并且只能是 public,用 private 修饰会报编译错误)。接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。抽象类和接口的区别?抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。一个类只能继承一个抽象类,而一个类却可以实现多个接口。

:JDK 1.8 以后,接口里可以有静态方法和方法体了。?

接口使用接口内的方法(抽象方法,默认实现方法,静态方法)publicinterfaceAInterface{//写属性publicintn1=10;//写方法(抽象方法,默认实现方法,静态方法)//在接口中,抽象方法可以省略abstract关键字publicvoidhi();//jdk8及之后版本,可以有默认实现方法,但需要用default关键字修饰defaultpublicvoidok(){System.out.println("ok");}//jdk8之后,可以有静态方法,不需要使用defaultpublicstaticvoidrun(){System.out.println("run");}}示例publicclassInterFaceDetail{}interfaceIA{voidsay();//默认publicvoidhi();}classCatimplementsIA{// Ctrl + i 快速实现接口的方法@Overridepublicvoidsay(){}@Overridepublicvoidhi(){}}

抽象类实现接口时,可以不实现接口的方法

一个类可以实现多个接口,

interfaceIB{}interfaceIC{}classDogimplementsIB,IC{}

接口不能继承其它的类,但是可以继承多个别的接口,使用extends而不是implements?

interfaceIDextendsIB,IC{}

接口VS继承

继承的价值主要在于:解决代码的复用性和可维护性

接口的价值注意在于:设计好各种规范(方法),让其他类取实现这些方法,更加的灵活,可以视为对单继承的补充

继承是满足 is a 的关系(猫是动物),而接口只需满足 like a 的关系 (猫像鱼一样游泳)?

publicclassInterFaceDetail02{publicstaticvoidmain(String[]args){littlemonkey littlemonkey=newlittlemonkey("悟空");littlemonkey.climbing();littlemonkey.swiming();littlemonkey.flying();}}classmonkey{privateStringname;publicmonkey(Stringname){this.name=name;}publicStringgetName(){returnname;}publicvoidclimbing(){System.out.println("猴子"+name+"会爬树");}}interfaceSwimming{voidswiming();}interfaceFlying{voidflying();}classlittlemonkeyextendsmonkeyimplementsFlying,Swimming{//可以继承类的同时,实现多个接口的方法@Overridepublicvoidswiming(){System.out.println("通过学习,"+getName()+"可以像鱼儿一样游泳");}@Overridepublicvoidflying(){System.out.println("通过学习,"+getName()+"可以像鸟儿一样飞翔");}publiclittlemonkey(Stringname){super(name);}}接口多态特性

如下例,只要是实现了USBInterface接口方法的类都可以作为参数传递给USBinterface

publicclassInterfacePoly{publicstaticvoidmain(String[]args){Cameracamera=newCamera();Phonephone=newPhone();Computercomputer=newComputer();computer.work(phone);//这里由于work的参数类型为USBInterface,所以参数可以传进它的子类computer.work(camera);}}publicclassComputer{publicvoidwork(USBInterfaceusbInterface){//通过接口,调用方法 体现多态性 固定接口的方法格式usbInterface.start();usbInterface.stop();}}publicclassPhoneimplementsUSBInterface{@Overridepublicvoidstart(){System.out.println("手机开始工作");//对接口做具体的实现}@Overridepublicvoidstop(){System.out.println("手机停止工作");}}

再比如,接口类型可以指向所有实现了接口内方法的类的对象

interfaceIF{}classMonsterimplementsIF{}classCarimplementsIF{}publicstaticvoidmain(String[]args){IF if01=newMonster();//创建接口类型的变量,指向Monster类型 相当于向上转型if01=newCar();//接口类型可以 指向实现了接口的类的对象实例}

可以创建接口类型的数组,对于不同位置的元素放不同的实现接口的类

USBInterfaceusb[]=newUSBInterface[2];//多态数组usb[0]=newPhone();usb[1]=newCamera();for(inti=0;i<usb.length;i++){usb[i].start();usb[i].stop();if(usb[i]instanceofPhone){((Phone)usb[i]).call();}}接口多态的传递?

如果IG继承了IH接口,而Cat类实现了IG接口,那么实际上相当于Cat类也实现了IH接口

内部类可以直接访问外部类的所有成员,包括私有成员外部类只能先创建内部类对象,然后通过该对象调用内部类方法外部其他类不能创建内部类对象如果外部类和局部内部类的成员重名,默认遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员名局部内部类

局部内部类是定义在外部类的局部位置,通常是在外部类的方法中。

局部内部类不能加访问修饰符,因为他的地位就相当于局部变量。但可以加final,不被别的类继承

局部内部类的作用域仅仅在定义它的方法或代码块中。

外部类只能定义内部类的方法中创建内部类对象,然后通过该对象调用内部类方法

publicclassLocalInnerClass{publicstaticvoidmain(String[]args){Outer01outer01=newOuter01();outer01.out2();}}classOuter01{privateintn1=100;privatevoidout1(){}publicvoidout2(){//内部类只可以加 final 修饰符finalclassInner01{//内部类可以直接访问外部类的所有成员。包括私有的publicvoidin1(){System.out.println("内部类调用 n1="+n1);}}Inner01inn01=newInner01();inn01.in1();}}

外部其他类不能创建内部类对象

如果外部类和局部内部类的成员重名,默认遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员名

publicvoidout2(){//内部类只可以加 final 修饰符finalclassInner01{privateintn1=200;//内部类可以直接访问外部类的所有成员。包括私有的publicvoidin1(){System.out.println("内部类调用 n1="+this.n1);//Outer01.this本质就是外部类的一个对象,谁调用了out2方法,Outer01.this就指向哪个对象System.out.println("外部类调用 n1="+Outer01.this.n1);}}}匿名内部类?

本质是内部类,类的名字由系统分配,名字是外部类的名字 + $1

同时还是一个对象,系统实现类的定义和对接口的实现后,new了一个对象并把它的地址返回给了我们要接收的变量

由于匿名类没有类名,那么除了定义它的地方,其他地方无法调用,匿名内部类使用一次后就不可以再使用了

基于接口的匿名内部类如下,匿名内部类的声明是一个表达式,所以要以 ; 结尾

直接 new IA{ 在这里实现接口里的方法 };

publicclassAnnoymousInnerClass{publicstaticvoidmain(String[]args){Outer02outer02=newOuter02();outer02.method();}}classOuter02{privateintnum=10;publicvoidmethod(){//基于接口的匿名内部类//1.传统方式,是创建一个类Tiger,实现该接口 并创建对象//2.需求是 Tiger 只调用一次,后面不再使用 如果每次都定义一个类会造成资源浪费//3.使用匿名内部类简化开发//本质是继承了IA接口,然后对它要使用的方法进行了具体的实现IA tiger=newIA(){@Overridepublicvoidcry(){System.out.println("老虎叫唤");}};//注意这里要有分号//编译类型为 IA 运行类型为 匿名内部类,底层定义了这个类并直接创建了对象,把对象的this赋给了tiger/*底层class Outer02$1 implements IA{public void cry() {System.out.println("老虎叫唤");}}*/System.out.println("tiger的运行类型为"+tiger.getClass());//运行类型为 Outer02$1tiger.cry();}}interfaceIA{publicvoidcry();}

基于类的匿名内部类 本质是继承了Father类,然后对它要使用的方法进行了重写

classOuter02{privateintnum=10;publicvoidmethod(){//基于接口的匿名内部类//1.传统方式,是创建一个类Tiger,实现该接口 并创建对象//2.需求是 Tiger 只调用一次,后面不再使用 如果每次都定义一个类会造成资源浪费//3.使用匿名内部类简化开发IA tiger=newIA(){@Overridepublicvoidcry(){System.out.println("老虎叫唤");}};System.out.println("tiger的运行类型为"+tiger.getClass());tiger.cry();//基于类的匿名内部类,与接口的区别在于()内可以有参数//father的编译类型 Father 运行类型 为匿名内部类 Outer02$2Fatherfather=newFather("jack"){};System.out.println("father的运行类型为"+father.getClass());}}classFather{publicFather(Stringname){//构造器}publicvoidtest(){}}

基于类的匿名内部类与普通的定义类区别

//匿名内部类的实现关键在于 后面的 {}; 本质是继承了Father类,然后对它要使用的方法进行了重写Fatherfather1=newFather("jack"){};//这里可以什么都不做,即没有对Father类的方法进行重写System.out.println("father的运行类型为"+father1.getClass());//运行类型为 匿名内部类 Outer02$2 按顺序编号//普通的new一个对象实例并赋地址给 father2Fatherfather2=newFather("jack");System.out.println("father的运行类型为"+father2.getClass());//运行类型为FatherclassFather{publicFather(Stringname){//构造器}publicvoidtest(){}}

基于抽象类的匿名内部类,与上类似,只不过{}内必须把抽象类的方法做出具体实现。

匿名内部类存在的前提是要有继承或者实现关系的,但是并没有看到extends和implements关键字,由底层JVM实现?

匿名内部类其实还是一个对象,系统实现类的定义和对接口的实现后,new了一个对象并把它的地址返回给了我们要接收的变量

如上基于接口的匿名内部类

newIA(){@Overridepublicvoidcry(){System.out.println("老虎叫唤");}}.cry();//当做对象直接调用方法

可以直接访问外部类的所有成员,包括私有成员

不能添加访问修饰符,因为它的地位就是一个局部变量

作用域仅仅在定义它的方法或者代码块中

外部其他类不可以访问匿名内部类

如果外部类和匿名内部类的成员重名,默认遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员名

如果匿名内部类重新定义了它所继承的父类的属性(前提是父类的属性不为private)那么调用

classOuter02{privateintnum=10;publicvoidmethod(){newFather("jack"){//private int num = 20;@Overridepublicvoidtest(){System.out.println("匿名内部类调用的num="+num);//首先看内部类有没有重新定义num,没有则取继承的父类即Father找是否有num且可以访问,还没有则找外部类的num//System.out.println("Father类的 num=" + Outer02.this.num); 这是调用外部类的num值// Outer02.this 就是调用了method方法的对象}}.test();//当成对象直接调用方法}}classFather{protectedintnum=30;publicFather(Stringname){//构造器}publicvoidtest(){}}最佳实践,匿名内部类可以直接当成参数使用publicstaticvoidmain(String[]args){//匿名内部类可以当做实参直接传递f1(newIG(){@Overridepublicvoidshow(){System.out.println("这是一幅名画");}});}//静态方法 为了直接在main里调用publicstaticvoidf1(IG ig){ig.show();}}interfaceIG{voidshow();}

实例二

publicclassInnerClassExercise02{publicstaticvoidmain(String[]args){//测试手机的闹钟功能,通过匿名内部类(对象)作为参数,打印懒猪起床了 和 小伙伴上课了CellPhonecellPhone=newCellPhone();//这里的函数参数是一个实现了Bell接口的匿名内部类 重写了ring方法cellPhone.alarmclock(newBell(){@Overridepublicvoidring(){System.out.println("懒猪起床了");//运行类型为 InnerClassExercise02$1}});cellPhone.alarmclock(newBell(){@Overridepublicvoidring(){System.out.println("小伙伴上课了");//运行类型为 InnerClassExercise02$2}});}}//铃声接口interfaceBell{voidring();}classCellPhone{//闹钟功能publicvoidalarmclock(Bellbell){//这里传入的bell 编译类型为Bell 运行类型为调用时创建的匿名内部类(是个对象)bell.ring();//调用ring方法,首先去自己的运行类型里找,运行类型里重写了ring方法,所以调用自己重写后的ringSystem.out.println("运行类型为"+bell.getClass());}}成员内部类

没有定义在外部类的方法中,而是定义在外部类的成员的位置上

publicclassMemberInnerClass{publicstaticvoidmain(String[]args){Outer03outer03=newOuter03();outer03.T();}}classOuter03{privateintn1=10;publicStringname="张三";//成员内部类,没有定义在外部类的方法中classInner03{publicvoidsay(){//可以直接访问外部类的所有成员,包括私有成员System.out.println("name="+name+" n1="+n1);}}//使用成员内部类publicvoidT(){Inner03inner03=newInner03();inner03.say();}}

可以直接访问外部类的所有成员,包括私有成员

可以添加任意访问修饰符(public,private,protected,默认),因为它的地位是一个成员(这一点与局部内部类和匿名内部类不同)

作用域为整个外部类(因为成员内部类就是外部类的一个成员),这一点与局部内部类和匿名内部类也不同

外部类要访问成员内部类,必须先创建对象,再通过对象访问方法

外部其他类也可以使用成员内部类(与上面两个也不同)

外部其它类使用成员内部类的三种方式第一种Outer03outer03=newOuter03();Outer03.Inner03inner031=outer03.newInner03()//把内部类当成成员第二种

在外部类中编写一个方法,返回创建好的成员内部类对象实例

publicclassMemberInnerClass{publicstaticvoidmain(String[]args){Outer03outer03=newOuter03();//Outer03.Inner03 inner031 = outer03.new Inner03(); 方式1Outer03.Inner03inner032=outer03.getInner03Instance;//方式2}}classOuter03{classInner03{publicvoidsay(){//可以直接访问外部类的所有成员,包括私有成员}}publicInner03getInner03Instance(){returnnewInner03();}}第三种Outer03.Inner03inner033=newouter03().newInner03()//其实就是第一种两句合成了一句

如果外部类和成员内部类的成员重名,默认遵循就近原则,如果想访问外部类的成员,使用 外部类名.this.成员名

静态内部类publicclassStaticInnerClass{publicstaticvoidmain(String[]args){Outer04outer04=newOuter04();outer04.m();}}classOuter04{privateintn1=10;publicstaticStringname="张三";//同成员内部类,静态内部类的地位也是外部类的一个成员staticclassInner04{publicvoidsay(){System.out.println(name);}}//定义方法去创建内部类对象并调用内部类的方法publicvoidm(){Inner04inner04=newInner04();inner04.say();}}

放在外部类的成员的位置,但添加了static修饰符

可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员

可以添加任意的访问修饰符,因为它的地位就是一个成员。

外部类要访问成员内部类,必须先创建对象,再通过对象访问方法

作用域:为整个外部类体

如果外部类和局部内部类的成员重名,默认遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员名

外部其他类也可以使用成员内部类(直接通过内部类名 前提访问控制不为private)

方式1,直接通过外部类名访问到内部类,前提要有访问权限//这里解释一下 首先是 Outer04.Inner04()作为一个整体 相当于new了一个Outer04.Inner04() 创建了一个内部类对象Outer04.Inner04inner04=newOuter04.Inner04();//之前成员内部类是 先new了外部类在new了内部类inner04.say();方式2,编写一个方法,返回静态内部类的对象实例Outer04outer04=newOuter04();//创建外部类对象实例Outer04.Inner04inner041=outer04.getInner04();//利用外部类对象调用它的返回静态内部类的对象的方法inner041.say();publicInner04getInner04(){returnnewInner04();}方式3,编写一个静态方法(可以直接用外部类名 . 静态方法名调用),返回静态内部类的对象实例//以下方法更简洁,充分利用了静态内部类的静态特性,Outer04.Inner04inner04_=Outer04.getInner04_();inner04_.say();publicstaticInner04getInner04_(){returnnewInner04();}

枚举类

枚举类的对象是确定的,只有有限个。例如,如果把季节定义成类,那么这个类只有四个对象:春夏秋冬。此时就能把季节定义为一个枚举类,这个枚举类的对象是确定的并且只有有限个。需要定义一组常量时,推荐使用枚举类。如果枚举类只有一个对象,则可以作为一种单例模式的实现方式。自定义枚举类

第一步,构造器私有化,防止直接new

第二步,去掉set方法,防止属性被修改

在类内部,直接创建固定对象

publicstaticfinalSeasonSPRINT=newSeason("春天","温暖");publicstaticfinalSeasonSUMMER=newSeason("夏天","炎热");publicstaticfinalSeasonAUTUMN=newSeason("秋天","凉爽");publicstaticfinalSeasonWINTER=newSeason("冬天","寒冷");使用enum关键字实现枚举类将类的定义 class 改为 enumenumSeason2{//使用enum关键字SPRINT("春天","温暖"),SUMMER("夏天","炎热");//这里的两个参数对应构造器的两个参数privateStringname;privateStringdesc;//描述privateSeason2(Stringname,Stringdesc){this.name=name;this.desc=desc;}@OverridepublicStringtoString(){return"Season2{"+"name="+name+\+", desc="+desc+\+};}}如果有多个常量(对象),使用,间隔如果用enum实现枚举,要求将定义的常量对象写在最前面。位置固定,不然报错本质:定义 enum Season2 实际上是 Season2 继承了 java 的java.lang.Enum类,而且是一个final类,然后定义了对应的public static final常量由于enum已继承了Enum类,所以不能加extends关键字了,但可以继承接口,加implementsjavap反编译//命令行界面,使用javap 命令对生成的.class文件进行反编译D:\JetBrains\IDEA\enum_annotation\out\production\enum_annotation\com\svicen\enum_>javapSeason2.classCompiledfrom"EnumExercise02.java"finalclasscom.svicen.enum_.Season2extendsjava.lang.Enum<com.svicen.enum_.Season2>{publicstaticfinalcom.svicen.enum_.Season2SPRINT;publicstaticfinalcom.svicen.enum_.Season2SUMMER;publicstaticcom.svicen.enum_.Season2[]values();publicstaticcom.svicen.enum_.Season2valueOf(java.lang.String);publicjava.lang.StringgetName();publicjava.lang.StringgetDesc();publicjava.lang.StringtoString();static{};}

如果使用无参构造器创建枚举对象,小括号也可以省略。WINTER即可

如果enum 枚举类内没有重写 toString方法,则回去调用父类的toString方法,父类即Enum类,直接访问它定义的名字

//Enum类的属性由 name 和 ordinal 构造器如下protectedEnum(Stringname,intordinal){this.name=name;//这里的name即为定义时的SPRING SUMMER 等 连同索引作为参数传给了父类的构造器this.ordinal=ordinal;}publicStringtoString(){returnname;//上面的Season如果将toString注释,输出Season2.SPRINT 即为 SPRING}

Enum类的常用方法

方法名称描述values()以数组形式返回枚举类型的所有成员 调用时用枚举类名.values()valueOf()将普通字符串转换为枚举实例,要求字符串为已有的常量compareTo()比较两个枚举成员在定义时的顺序(索引),大于返回1,等于返回0,小于返回-1ordinal()获取枚举对象的索引位置(从0开始)name()获取枚举对象的名字toString()Enum类已经重写过了,返回的是当前对象的对象名,子类可以重写该方法System.out.println("name="+Season2.SPRINT.name());System.out.println("ordinal="+Season2.AUTYMN.ordinal());Season2[]values=Season2.values();for(Season2season:values){System.out.println(season);}Season2summer=Season2.valueOf("SUMMER");System.out.println("summer.ordinal="+summer.ordinal());System.out.println(summer.compareTo(Season2.SUMMER));//结果为0 比较的就是ordinal

注解

Java 注解(Annotation)又称 Java 标注,Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

内置的注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

作用在代码的注解是

@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

@Target(ElementType.METHOD)//标记这个注解应该是哪种 Java 成员//标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。@Retention(RetentionPolicy.SOURCE)public@interfaceOverride{}

@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。

@SuppressWarnings - 指示编译器去忽略注解中声明的警告。一直警告(黄色提示)

@SuppressWarnings({"all"})//注意它有作用范围,方法或类上面作用于方法或类内部//该注解类内部有一个属性String[] value(); 所以传的时候可以传多种警告,比如@SuppressWarnings({"all","unused"})//作用于@SuppressWarnings的注解@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,MODULE})

作用在其他注解的注解(或者说元注解)是:

@Retention-标识这个注解怎么保存,在编译代码中(RetentionPolicy.SOURCE),还是编入class文件中(RetentionPolicy.CLASS)(默认是这个),或者是在运行时可以通过反射访问(RetentionPolicy.RUNTIME)(运行时还保存,保留时间最长)。@Documented - 标记这些注解是否包含在用户文档中。生成javaDOC时该注解会被保留在文档上@Target -标记这个注解可以修饰的 Java 成员。@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

从 Java 7 开始,额外添加了 3 个注解:

@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

异常

执行过程发生的异常有两大类

Error,Java虚拟机无法解决的严重问题,如JVM系统内部错误,资源耗尽等严重情况。比如StackOverflowError和OOM(out of memeory),会导致程序的崩溃Exception:其它因为编译错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码对其进行处理。例如空指针访问,识图读取不存在的文件,网络连接中断等等。Execption分为两大类,运行时异常和编译时异常。运行时异常:RunTimeException:有NullPointerException,ArithmeticException(分母为0),有默认处理机制ArrayIndexOutOfBoundsException,ClassCastException(类型转换),NumberFormatException(数字格式异常)等编译时异常:FileNotException:ClassNotFoundException(加载不存在的类),必须处理

进入Throwable后查看源码,右键Diagrams -> show Diagrams

异常处理方式

try-catch-finally Ctrl+alt +t 选择异常

intnum1=1,num2=0;try{intnum3=num1/num2;//可能有异常的代码块}catch(Exceptione){//当异常发生时,系统将异常封装到Exception对象e,传递给catch,程序员可以自己处理//没有发生异常,catch代码块是不会执行的//throw new RuntimeException(e);System.out.println("异常为"+e.getMessage());}finally{//不管try代码块有没有发生异常,始终要执行finally代码块,//通常将释放资源的代码放在这里}

throws

将发生的异常抛出,交由调用者处理,最终JVM调用了main函数,每一步都有两种处理方式。默认throws(二选一即可)

try-catch细节如果发生异常,则异常发生后面的代码不会执行,直接进入到catch代码块try{Stringstr="异常";inta=Integer.parseInt(str);System.out.println("转换后:"+a);//不会执行}catch(NumberFormatExceptione){System.out.println("异常信息:"+e.getMessage());}finally{System.out.println("finally代码块执行");}System.out.println("程序继续执行");//会执行

如果异常没有发生,则顺序执行try的代码块,不会进入到catch

如果希望无论是否发生异常都执行某段代码(比如断开连接、释放资源),可以加上finally

try代码块可以有多个异常,可以有多个catch语句,分别捕获不同异常,要求子类异常要写在前面,父类异常(Exception e)写在后面

可以进行try-finally配合使用,应用于:执行一段代码,不管是否发生异常,都必须执行某个业务逻辑

相当于没有捕获异常,执行完finally的语句后程序会直接崩掉/退出

try{intn1=10;intn2=0;System.out.println(n1/n2);}finally{System.out.println("finally语句块执行...");}System.out.println("程序继续执行");//这条语句不会执行//如下返回结果为4,即使捕获到了NullPointerException异常,但是catch下的return语句还是没有执行,执行了finally语句//注意 catch下的return不会执行,但是 相关的语句还是会执行的 比如 return ++i; i还是会完成自增,但不会返回

注意:下面的catch语句块中会将++i后的值作为临时变量先保存起来再去执行finally语句,最后返回这个临时变量

throws细节对于编译异常,程序中必须处理。 快捷键 : alt + 回车对于运行时异常,程序中如果没有处理,默认是throws如果一个方法可能生成某种异常,但是并不能确定如何处理这些异常,则此方法应显示地声明抛出异常,表名该方法将不对异常进行处理,而由该方法的调用者负责处理。在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的具体异常类型,也可以是它的父类,也可以是一个异常列表,即可以抛出多个异常,如下所示publicclassThrows01{publicstaticvoidmain(String[]args){}publicvoidf1()throwsFileNotFoundException,NullPointerException,ArithmeticException{//创建了一个文件流对象,//这里的异常是一个FileNotFoundException,编译时异常//可以使用try-catch-finally//使用throws,抛出异常 throws FileNotFoundException 也可以是 throws ExceptionFileInputStreamfileInputStream=newFileInputStream("d//aa.txt");//编译异常}}子类重写父类方法时,对抛出的异常的规定:子类重写的方法所抛出的异常要么和父类抛出的异常一致,要么为父类抛出异常的子类型,范围不能大于父类的异常范围。比如说父类抛出RunTimeException,子类抛出Exception是不允许的。如果f3调用f1,f1里有编译异常并抛出给调用者,f3里必须对异常进行处理,或者try-catch或者throws,但是如果f1有运行异常则f3可以不处理,默认是throws自定义异常

一般是继承RunTimeException

publicclassThrows01{publicstaticvoidmain(String[]args){intage=200;if(!(age>=18&&age<=120)){thrownewAgeException("年龄需在18-120");//抛出异常,输出的即为我们这里传的参数}System.out.println("你的年龄范围正确");}}classAgeExceptionextendsRuntimeException{publicAgeException(Stringmessage){//构造器super(message);}}意义位置后面跟的东西throw手动生成异常对象的关键字,与new并用方法体中异常对象throws异常处理的一种方式方法声明处异常类型

包装类

所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。

这种由编译器特别支持的包装称为装箱,所以当内置数据类型被当作对象使用的时候,编译器会把内置类型装箱为包装类。相似的,编译器也可以把一个对象拆箱为内置类型。Number 类属于 java.lang 包。

//手动装箱 jdk5之前手动装箱和拆箱intn1=10;Integerinteger=newInteger(n1);// jdk9以后 @Deprecated方法过时//手动拆箱inti=integer.intValue();//自动装箱 jdk5之后可以intn2=20;Integerinteger2=n2;System.out.println(n2);//自动拆箱intn3=integer2;System.out.println(n3);//注意看以下代码 三元运算符是一个整体,虽然执行new Integer(1)但整体精度为double,输出1.0Objectobj1=true?newInteger(1):newDouble(2.0);System.out.println(obj1);// 1.0包装类方法

包装类转字符串

Integeri=100;//方式1Stringstr1=i+"";//方式2Stringstr2=i.toString();//方式3Stringstr3=String.valueOf(i);

字符串转包装类

//String->WrapperStringstr4="123";Integeri2=Integer.parseInt(str4);//方式2Integerinteger=newInteger(str4);

经典题目

Integerm=1;Integern=1;System.out.println(m==n);//true 这里没有new对象Integerx=128;Integery=128;System.out.println(x==y);//false 这里返回的是new的对象intn=10;Integernn=10;System.out.println(n==nn);//false 只要有基本数据类型判断的就是值是否相等//Integer m = 1; 底层是调用了Integer的 valueOf方法,publicstaticIntegervalueOf(inti){if(i>=IntegerCache.low&&i<=IntegerCache.high)returnIntegerCache.cache[i+(-IntegerCache.low)];returnnewInteger(i);}publicstaticIntegervalueOf(inti){if(i>=IntegerCache.low&&i<=IntegerCache.high)//low为-128 high为127 Integer的缓存机制returnIntegerCache.cache[i+(-IntegerCache.low)];returnnewInteger(i);}

String类

String类实现了Comparable(对象可以比较)和Serializable接口(说明String可以串行化,可以在网络传输)

String的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节

String类有很多构造器的重载,常用的有

Strings1=newString();Strings2=newString(Stringoriginal);Strings3=newString(char[]a);Strings4=newString(char[]a,intstartIndex,intcount);Strings5=newString(byte[]b);String是final类,不能被其他的类继承,String有属性,private final byte[] value; 用于存放字符串内容,value数组也有final关键字,不可以修改(指的是value[]不可以指向新的地址,但具体的字符是可以改的)。对象的创建//1、直接在常量区找是否有"svicen"的数据空间,有则将s1指向该空间,没有则重新创建,然后指向。s1最终指向的常量池的空间地址Strings1="svicen";//2、现在堆中创建空间,里面维护了value属性指向常量池的"svicen"的数据空间,如果常量池没有则创建后指向。s2指向的是堆的空间地址Strings2=newString("svicen");

String的intern方法,如果池已经包含一个等于此String对象的字符串(用equals(Object)判断),则返回池中的字符串。否则,将此String对象添加到池中,并返回此对象的引用。最终返回的是常量池的地址

经典题目

Personp1=newPerson();//在堆区创建了两个Person对象,他的name属性时字符串,字符串的value指向常量区的"svicen"p1.name="svicen";Personp2=newPerson();p2.name="svicen";System.out.println(p1.name.equals(p2.name));//trueSystem.out.println(p1.name==p2.name);//true 只要常量区里有"svicen"就直接指向,所以p1 p2的value指向的地址相同System.out.println(p1.name=="svicen");//true p1.name的value[]指向的就是常量区的"svicen"的地址Strings1=newString("abc");Strings2=newString("abc");System.out.println(s1==s2);//false 这里新创建了两个对象,对象的地址一定是不一样的System.out.println(s1.intern()==s2.intern());//true intern方法返回了两个对象的value指向的常量区的地址//关于字符串对象创建的问题Strings1="hello";s1="haha";//创建了两个对象 因为字符串的值不能改变,修改后相当于新创建了一个对象让s1指向它Stringa="hello"+"abc";//创建一个对象,编译器会做优化,判断创建的常量区对象是否有引用指向,//等价于 String a = "helloabc"Stringa="hello";Stringb="abc";Stringc=a+b;//创建三个对象 // d = "helloabc"//1.先创建一个StringBuilder sb = StringBuilder()//2.执行sb.append("hello");//3.执行sb.append("abc");//4.执行sb.toString()方法,return 上面的字符串 返回给c//最后实际上是 c 指向堆中的对象(String) value[] value数组再指向 常量池的"helloabc" 所以 c==d 返回false// 字符串常量相加结果直接指向常量池 字符串变量相加结果指向堆的内存String常用方法intlength():返回字符串的长度:returnvalue.lengthcharcharAt(intindex):返回某索引处的字符returnvalue[index]不要使用str[0]取第一个字符booleanisEmpty():判断是否是空字符串:returnvalue.length==07StringtoLowerCase():使用默认语言环境,将String中的所有字符转为小写StringtoUpperCase():使用默认语言环境,将String中的所有字符转为大写char[]toCharArray():将String转换成一个char数组booleanequals(Objectobj):比较字符串的内容是否相同booleanequalsIgnoreCase(Stringstr)功能与equals相似,忽略大小写Stringconcat(Stringstr):将指定字符串连接到此字符串的结尾,等价于”+“intcompareTo(Stringstr):比较两个字符串的大小,前者大返回正数,后者大返回负数,长度和内容都相同返回0Stringa="jacks";Stringb="jaaks";System.out.println(a.compareTo(b));//每一位依次比较,最终返回 c - a = 2a="jaak";System.out.println(a.compareTo(b));//如果二者已比较的的都相等,但长度不等,最后返回的是二者的长度的差值Stringsubstring(intbeginIndex):返回一个新的字符串,它是此字符串的从beginIndex位置开始截取Stringsubstring(intbeginIndexintendIndex):返回一个新的字符串,它是此字符串的从beginIndex位置开始截取到endIndex位置,booleanendsWidth(Stringstr):测试此字符串是否以指定的后缀结束booleanstartsWidth(Stringstr):测试此字符串是否以指定的前缀开始booleanstartsWidth(Stringstr,inttoffset):测试此字符串是否在指定索引开始的位置以指定的前缀开始booleancontains(CharSequences):当且仅当此字符串包含指定的char值序列时返回trueintindexOf(Stringstr):返回指定子字符串在此字符串中第一次出现的索引,找不到返回-1intindexOf(Stringstrintindex):返回指定子字符串在此字符串中第一次出现的索引,从指定索引处开始查找intlastIndexOf(Stringstr):返回指定子字符串在此字符串中最右边出现的索引,找不到返回-1intlastIndexOf(Stringstr):返回指定子字符串在此字符串中最右边出现的索引,从指定索引处开始查找Stringreplace(charoldChar,charnewChar):返回一个新的字符串,它是通过newChar替换此字符串中出现的所有oldChar得到的s2=s1.replace("a","b");//对于s1本身没有影响Stringreplace(CharSequencetarget,CharSequencereplacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串StringreplaceAll(Stringregex,Stringreplacement):使用指定的replacement代替正则表达式查询出来的值StringreplaceFirst(Stringregex,Stringreplacement):使用指定的replacement代替正则表达式查询出来的第一个值booleanmatches(Stringregex):告知此字符串是否匹配给定的正则表达式String[]split(Stringregex):根据给定正则表达式的匹配拆分此字符串,s=s.split(",");以逗号为分割点分割String[]split(Stringregex,intlimit):根据匹配的正则表达式来拆分此字符串,最多不超过limit个,如果超过,剩下的全部放到最后一个元素中。Stringformat(Stringformat,Objectargs):就是利用%S%d%x占位符

JAVA的格式化字符串 format

String formatStr = "我的名字是%s,年龄是%d,成绩是%.2f,性别是%c.";String info = String.format(formatStr,a,age,score,sex);System.out.println(info);

由于String每次更新对象都需要重新开辟空间,效率较低,因此有StringBuilder和StringBuffer来增强String的功能

StringBuffer

StringBuffer的直接父类是AbstractStringBuilder,StringBuffer也实现了Serializable接口,即它创建的对象也可以串行化

在父类AbstractStringBuilder中有属性char[] value,不是final修饰的,所以该value数组存放在堆里,不再是常量池

StringBuffer也是一个final类,不可以被继承

StringBuffer保存的是字符串变量,里面的值可以修改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址

即创建新对象),所以效率要比String高

StringBuffer方法

构造器

//创建一个大小为16的char数组StringBufferstringBuffer=newStringBuffer();//通过构造器指定char value数组的大小StringBufferstringBuffer1=newStringBuffer(100);//通过构造器直接赋值一个字符串,char数组大小为16+length(传入的字符串)StringBufferhello=newStringBuffer("hello");

String与StringBuffer的转换

//String->StringBuffer//方法一:使用构造器,对str本身没有影响,只是返回的是一个StringBuffer对象Stringstr="svicen";StringBufferstringBuffer2=newStringBuffer(str);//方法二:使用append方法,同样对str本身没有影响,只是拷贝了一份str追加到了当前StringBuffer对象的末尾StringBufferstringBuffer=newStringBuffer();stringBuffer.append(str);//StringBuffer->String//方法一:使用StringBuffer的toString方法StringBufferhello=newStringBuffer("hello");Strings=hello.toString();//方法二:使用构造器Strings1=newString(stringBuffer2);

1、append方法

public StringBuffer append(boolean b)

该方法的作用是追加内容到当前StringBuffer对象的末尾,类似于字符串的连接。调用该方法以后,StringBuffer对象的内容也发生 改变。

2、insert方法

public StringBuffer insert(int offset, boolean b)

作用是在StringBuffer对象中插入内容,然后形成新的字符串。

3、delete()方法

s.delete(1,2); 删除1-2的字符,左闭右开

4、replace()方法

s.replace(1,2,a); 把索引为1的字符替换为 a

5、deleteCharAt方法

public StringBuffer deleteCharAt(int index)

该方法的作用是删除指定位置的字符,然后将剩余的内容形成新的字符串。

6、reverse方法

public StringBuffer reverse()

作用是反转StringBuffer对象中的内容,然后形成新的字符串。

7、trimToSize方法

public void trimToSize()

该方法的作用是将StringBuffer对象的中存储空间缩小到和字符串长度一样的长度,减少空间的浪费。

8、setCharAt方法

public void setCharAt(int index, char ch)

作用是修改对象中索引值为index位置的字符为新的字符ch。

题目实例,对金额形式的转换,每三位添加一个,

Scannerscanner=newScanner(System.in);Stringstr=scanner.next();StringBufferstringBuffer=newStringBuffer(str);for(inti=stringBuffer.lastIndexOf(".")-3;i>0;i-=3){//123456.59//先找到小数点位置stringBuffer.insert(i,",");}System.out.println(stringBuffer);

StringBuilder

一个可变的字符序列,此类提供一个与StringBuffer兼容的API,但不保证同步(会有线程安全问题),此类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候,如果可能,建议优先采用该类,因为大多数实现中,它比StringBuffer更快

StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据。

StringBuilder的直接父类是AbstractStringBuilder,StringBuilder也实现了Serializable接口,即它创建的对象也可以串行化仍是final类,对象字符序列仍是存放在父类AbstractStringBuilder的属性char[] value中,StringBuilder的方法,没有做互斥的处理,即没有synchronized关键字,因此在单线程的情况下使用

Math类

Math.sqrt(): 计算平方根

Math.cbrt(): 计算立方根

Math.pow(a, b): 计算a的b次方

Math.max( , ): 计算最大值

Math.min( , ): 计算最小值

Math.abs(): 取绝对值

Math.ceil(): 天花板的意思,就是逢余进一

Math.floor(): 地板的意思,就是逢余舍一

Math.rint(): 四舍五入,返回double值。注意.5的时候会取偶数

Math.round(): 四舍五入,float时返回int值,double时返回long值

Math.random(): 取得一个[0, 1)范围内的随机数

Arrays类

Arrays.fill(Object[ ] array, Object obj):用指定元素填充整个数组(替换数组原元素)

Arrays.sort(Object [ ]arr, new Comparator() ):对传入数组进行递增排序,字符则按照ASCII进行排序(不区分大小写),可以自己指定排序方法,传入一个接口Comparator实现定制排序(重写compare方法)。sort底层调用了匿名内部类Comparator的compare方法

Integer[]arr={1,5,9,3,6};Arrays.sort(arr);//默认升序System.out.println(Arrays.toString(arr));//1,3,5,6,9Arrays.sort(arr,newComparator<Integer>(){//接口编程@Overridepublicintcompare(Integero1,Integero2){returno2-o1;//降序排列}});System.out.println(Arrays.toString(arr));//9,6,5,3,1//源码阅读publicstatic<T>voidsort(T[]a,Comparator