一、Java基础语法
1、标识符和关键字
1. 标识符
-
Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符;
-
所有标识符都应该以字母(大小写都可以),美元符$,下划线_开头;
-
首字母之后可以是字母(大小写都可),美元符$,下划线_,或数字的任何字符组合;
-
不能使用关键字作为变量名或方法名;
-
标识符大小写是敏感的。
2. 关键字
abstract | assert | boolean | break | byte |
---|---|---|---|---|
case | catch | char | class | const |
continue | default | do | double | else |
enum | extends | final | finally | float |
for | goto | if | implements | import |
instanceof | int | interface | long | native |
new | package | private | protected | public |
return | strictfp | short | static | super |
switch | synchronized | this | throw | throws |
transient | try | void | volatile | while |
2、JAVA的数据类型
-
基本类型:byte、short、int、long float、double、char、boolean
-
引用类型:class、array、interface、enum、annotation、record(记录)
byte a=20;///占1个字节 -128~127
short b=300;//占2个字节 -32768~32767
int c=32800;///占4个字节
long d=125325996665655555L;//占8个字节,要在数字后加个L
float e=5.6F;//占4个字节 要在后面加个F
double f=4.2;//占8个字节
char g='a';///占两个字节
//String s="name";///不是关键字,是一个类
boolean h=true;///占1个字节
3、类型转换
-
由于Java是强类型语言,所以要进行某些运算时,需要用到类型转换。
数据类型有其优先级:
低级----------------------------------->高级
byte,short,char->int->long->float->double
其中,浮点类型相对于整数类型是高级的。byte、short、char相互之间是平级的。其余,高容量(字节范围)类型相对于低容量类型是高级的。
-
运算中,不同类型的数据先转换为同一类型,然后进行运算。
-
强制类型转换 (数据类型由高级向低级的转换称为强制类型转换,其语法为(类型)变量名)
-
自动类型转换 (数据类型由低级向高级的转换称为自动类型转换)
-
注意点:
-
不能对布尔值进行转换。
-
不能把对象类型转换为不相干的类型。
-
在把高容量转换到低容量的时候,强制转换
-
转换的时候可能存在内存溢出,或者精度问题!
-
低 -----------------------------------------------高
byte,short,char-> int-> long-> float-> double
int i=128;
byte b=(byte)i; //转换类型 : 强制转换 高 --低
// 强制转换 (类型) 高--低
// 自动转换 低--高
System.out.println(i); // 128
System.out.println(b); // -128 内存溢出 因为最大是-128~127
/*
注意点:
1. 不能对布尔值进行转换
2. 不能把对象类型转换为不相干的类型
3. 在把高容量转换到低容量的时候,强制转换
4. 转换的时候可能存在内存移除,或者精度问题
*/
System.out.println("===========");
System.out.println((int)23.7); // 23
System.out.println((int)-15.35f); // -15
System.out.println("==============");
char a='a';
int d=a+1;
System.out.println(d); // 低转高所以不需要强制转换 结果是98
System.out.println((char) d); //高转低 需要强制转换 结果是b
//操作比较大的数的时候 ,注意溢出问题
//jdk新特性,数字之间可以用下划线分割
System.out.println("================");
int duo = 10_0000_0000;
int nian =20;
int q=duo*nian; //计算溢出了 结果是-1474836480
long q1=duo*nian; //默认是int ,转换之前已经存在问题了
long q2=duo*((long) nian); //提升为long 运算解决问题输出200 0000 0000
System.out.println(q);
System.out.println(q1);
System.out.println(q2);
4、变量、常量、作用域
1. 变量
-
就是可以变化的量
-
Java是一种强类型语言,每个变量都必须声明其类型
-
Java变量是程序中最基本的存储单元,其要素包括 变量名 变量类型 和 作用域
注意事项
-
每个变量都有类型,类型可以是基本类型,也可以是引用类型
-
变量名必须是合法的标识符
-
变量声明是一条完整的语句,因此每一个声明都必须以分号结束
一般地,定义一个或多个变量,其语法为:
type varName [=value] [{,varName[=value]}];
//数据类型 变量名 = 值;
可以使用逗号隔开来声明多个同类型变量,也就是在同一行定义多个变量,但为使代码精简易读,即保证程序可读性,所以不建议这样做。
例:
int a=1;
int b=2;
int c=3;//分行书写,程序可读性良好
反例:
int a,b,c;//变量名未赋值
int a=1,b=2,c=3;//程序可读性差
2. 变量作用域
-
类变量
-
实例变量
-
局部变量
Java用一对大括号作为语句块的范围,称为作用域,在作用域里定义的变量,只有在该作用域结束之前才可使用。
一般地,根据作用域的不同,变量可分为以下三种:
-
类变量
有关键字”static”修饰,其作用域与实例变量相同。
例:
public class Test {
static double salary = 2500;
public static void main(String[] args) {
System.out.println(salary);
}
}
-
实例变量
无“static”修饰,从属于对象,即作用域为类(class)中,可以在方法的外面,无需初始化值,使用前需声明,语法如下例。
例:
public class Test {
String name;
int age;
public static void main(String[] args) {
//变量类型 变量名字 = new Test();
Demo08 demo08 = new Test();
System.out.println(Test.age);//0
System.out.println(Test.name);//null
}
}
如果不自行初始化值,那么这个变量的默认值为“0”(整数类型)、“0.0”(浮点类型)、“u0000”(字符类型)、“false”(布尔值)或“null”(除基本类型以外,其余的默认值)。
-
局部变量
写在方法(method,语句的集合,在一起执行一个功能)中,即它的作用域在方法后的大括号中,且在使用之前必须声明和初始化值。
例:
public static void main(String[] args) {
int i = 10;//声明和初始化值
System.out.println(i);//在作用域内使用
}
System.out.println(i);//超出作用域,无法使用
例:
public class Variable{
static int allClicks=0; //类变量
String str="hello world"; //实例变量
public void method(){
int i =0; //局部变量
}
}
3. 常量
-
常量:初始化后不能再改变值!不会变动的值
-
所谓常量可以理解成一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变
final 常量名 = 值; final double PI = 3.14;
-
常量名一般使用大写字符
public class Test {
//修饰符,不存在先后顺序
//final static double PI = 3.14;
static final double PI = 3.14;
public static void main(String[] args) {
System.out.println(PI);
}
}
4. 变量命名规范
-
所有变量、方法、类名:要求见名知意
-
类成员变量(除常量以外的变量):遵循首字母小写和驼峰原则(除了第一个单词以外,后面的单词首字母大写):monthlySalary(规范) lastname(不规范) lastName(规范)
-
常量:全部使用大写字母,单词之间使用下划线:MAX_VALUE
-
类名:首字母大写和驼峰原则:Man,TestTest
-
方法名:首字母小写和驼峰原则:test(),testTest()
5、基本运算符
-
算术运算符
-
关系运算符
-
位运算符
-
逻辑运算符
-
赋值运算符
-
其他运算符
1. 算数运算符
算术运算符用在数学表达式中,它们的作用和在数学中的作用一样。下表列出了所有的算术运算符。
public class Test01 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 25;
int d = 25;
System.out.println("a + b = " + (a + b) ); //30
System.out.println("a - b = " + (a - b) ); //-10
System.out.println("a * b = " + (a * b) ); //200
System.out.println("b / a = " + (b / a) ); //2
System.out.println("b % a = " + (b % a) ); //0
System.out.println("c % a = " + (c % a) ); //5
System.out.println("a++ = " + (a++) ); //10
System.out.println("a ="+ a); //11
System.out.println("a-- = " + (a--) ); //11
System.out.println("a ="+ a); //10
System.out.println("--d = " + (--d) ); //24
System.out.println("d ="+ d); //24
System.out.println("++d = " + (++d) ); //25
System.out.println("d ="+ d); //25
}
}
自增(++)自减(--)运算符是一种特殊的算术运算符,在算术运算符中需要两个操作数来进行运算,而自增自减运算符是一个操作数。 a++:a=a+1 a--:a=a-1 a++和++a的区别在于a++是先进行取值,后进行自增。++a是先进行自增,后进行取值。
2. 关系运算符
public class Test01 {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("a == b = " + (a == b)); //false
System.out.println("a != b = " + (a != b)); //true
System.out.println("a > b = " + (a > b) ); //false
System.out.println("a < b = " + (a < b) ); //true
System.out.println("b >= a = " + (b >= a) ); //true
System.out.println("b <= a = " + (b <= a) ); //false
}
}
3. 位运算符
-
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。
-
位运算符作用在所有的位上,并且按位运算。假设a = 60,b = 13;它们的二进制格式表示将如下:
public class Test01 {
public static void main(String[] args) {
/*
A =0011 1100(60)
B =0000 1101(13)
A&b 0000 1100 //相同为相同,不相同为0
A|B 0011 1101 //相同为相同,不相同为1
A^B 0011 0001 //相同为0不相同为1
~B = 1111 0010 //取反
2*8 = 16 2*2*2*2
效率极高!!
<< //左移 *2
>> //右移 /2
0000 0000 0
0000 0001 0
0000 0010 2
0000 0011 3
0000 0100 4
0000 1000 8
0001 0000 16
*/
System.out.println(2<<3); //0000 0010 2 ----> 0001 0000(16)
}
}
4. 逻辑运算符
-
下表列出了逻辑运算符的基本运算,假设布尔变量A为真,变量B为假
public class Test01 {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
System.out.println("a && b = " + (a&&b)); //false
System.out.println("a || b = " + (a||b) ); //true
System.out.println("!(a && b) = " + !(a && b)); //true
}
}
-
当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了。
5. 赋值运算符
public class Test01 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 0;
c = a + b;
System.out.println("c = a + b = " + c );//30
c += a ;
System.out.println("c += a = " + c );//40
c -= a ;
System.out.println("c -= a = " + c );//30
c *= a ;
System.out.println("c *= a = " + c );//300
a = 10;
c = 15;
c /= a ;
System.out.println("c /= a = " + c );//1
a = 10;
c = 15;
c %= a ;
System.out.println("c %= a = " + c );//5
c <<= 2 ;
System.out.println("c <<= 2 = " + c );//20
c >>= 2 ;
System.out.println("c >>= 2 = " + c );//5
c >>= 2 ;
System.out.println("c >>= 2 = " + c );//1
c &= a ;
System.out.println("c &= a = " + c );//0
c ^= a ;
System.out.println("c ^= a = " + c );//10
c |= a ;
System.out.println("c |= a = " + c );//10
}
}
6. 条件运算符
//条件运算符也被称为三元运算符。该运算符有3个操作数,
//并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
public class Test01 {
public static void main(String[] args){
int a , b;
a = 10;
// 如果 a 等于 1 成立,则设置 b 为 20,否则为 30
b = (a == 1) ? 20 : 30;
System.out.println( "Value of b is : " + b ); //30
// 如果 a 等于 10 成立,则设置 b 为 20,否则为 30
b = (a == 10) ? 20 : 30;
System.out.println( "Value of b is : " + b ); //20
}
}
7. 字符串连接+
public class Test01 {
public static void main(String[] args){
int a = 10;
int b = 20;
a+=b; // a=a+b
a-=b; // a=a+-b
System.out.println(a);//10
//字符串连接符 + ,String(字符串)
System.out.println(a+b);//30
System.out.println(""+a+b);//1020
System.out.println(a+b+"");//30
}
}
8. 总结
运算符优先级
二、Scanner用户交互
1、Scanner类的常用方法
nextlnt():只读取int值,就是只能读取整数类型的数据,如果输入了非整型的数据(浮点型字符串等)就会报错。 nextFloat()、nextDouble()这些也是以此类推,只能读取符合该类型的数据。
1.next()和nextLine()的区别
-
通过Scanner类的next()与nextLine()方法读取存储输入的字符串
-
next():
-
一定要读取到有效字符后才可以结束输入
-
对输入有效字符前的空格,会自动将其去掉
-
输入有效字符后,以其后面输入的空格作为结束符
-
next()不能得到带有空格的字符串
-
-
nextLine():
-
以Enter为结束符,存储的是输入回车之前的所有字符
-
可以获得空白字符
-
是实际中比较常用的方法
-
-
-
读取前一般需要使用hasNext()与hasNextLine()来判断后面是否还有输入的数据(有时可以省略不用)
public class Test {
public static void main(String[] args) {
// 创建Scanner的基本对象
Scanner scanner=new Scanner(System.in);
// 从键盘接收数据
//next方式接收字符串
System.out.println("next方式接收:");
// 判断是否还有输入
if (scanner.hasNext()) {
String str = scanner.next();
System.out.println("输入的数据为:" + str);
}
scanner.close();
}
}
class Test2{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 从键盘接收数据
// nextLine方式接收字符串
System.out.println("nextLine方式接收:");
// 判断是否还有输入
if (scan.hasNextLine()) {
String str2 = scan.nextLine();
System.out.println("输入的数据为:" + str2);
}
scan.close();
}
}
运行结果:
2、Scanner类的常用方法2
1.多条数据的输入
第一步:导入sacnner类 import Java.until.*;
第二步:创建Scanner对象 Sacnner scanner=new Scanner(system.in);//构造Scanner类的对象sc,接收从控制台输入的信息
第三步:打印输出提示符号 System.out.println("提示文字);
第四步:获取键盘输入的数据 String src=input.nextlnt()/nextLine();
第五步:打印输出值:System.out.println("打印输出的值为:");
第六步:hasNext()/hasnextLine()来判断是否还有下一个输入。
public class Test01 {
public static void main(String[] args){
Scanner scan = new Scanner(System.in); //构造Scanner类的对象scan,接收从控制台输入的信息
System.out.println("请输入你的姓名");
String name = scan.nextLine();//接收一个字符串,可以加除Enter以外的所有符号,包括空格和Tab
System.out.println("请输入你的ID");
String ID ;
while(scan.hasNextLine()) {// hasNextLine()方法判断当前是否有输入,当键盘有输入后执行循环
if(scan.hasNextInt()) {// 判断输入的值是否为整数类型,当为整数类型时执行循环
ID = scan.nextLine();
System.out.println("你输入的姓名为:"+name);
System.out.println("你输入的ID为:"+ID);
break;
}else {
System.out.println("请输入数字哦!");
ID = scan.nextLine();
continue;
}
}
}
}
//键入多个数字,求输入数字的平均值以及总和。在输入数值的时候需要通过回车键来进行确认,
//并且通过非数值来结束,结束时输出对应的总和以及平均数的计算结果。
public class Test01 {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
double sum = 0;
int m = 0;
System.out.println("请输入一个数字,输入非数字退出!");
//进行while循环,只要是数字就一直输入,否则退出
while (scan.hasNextDouble()) {
double x = scan.nextDouble();
m = m + 1;
sum = sum + x;
}
System.out.println(m + "个数的和为" + sum);
System.out.println(m + "个数的平均值是" + (sum / m));
scan.close();
}
}
运行结果:
三、流程控制
1、 流程控制概述
在Java程序中,JVM默认总是顺序执行以分号;结束的语句。但是,在实际的代码中,程序经常需要做条件判断、循环,因此,需要有多种流程控制语句,来实现程序的跳转和循环等功能。流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块。
其流程控制方式采用结构化程序设计中规定的三种基本流程结构,即:
-
顺序结构
-
选择结构
-
循环结构
2、顺序结构
顺序结构是程序中最简单最基本的流程控制,没有特定的语法结构,按照代码的先后顺序,依次执行,程序中大多数的代码都是这样执行的。
总的来说:程序按照从上到下的顺序执行。
-
JAVA的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行。
-
顺序结构就是最简单的算法结构。
-
语句与语句之间,框与框之间是按从上到下的顺序进行的,它是由若干个依次执行的处理步骤组成的,它是任何一个算法都离不开的一种基本算法结构。
public class Test {
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
System.out.println(3);
}
运行结果:
3、 选择结构
根据条件,选择性地执行某段代码。
有if…else和switch-case两种分支语句。
1. if单选择语句
-
语法:
条件表达式结果为boolean类型,当表达式结果为true,则执行语句块;否则什么都不执行! 当语句块只有一条执行语句时,一对{}可以省略,但建议保留。
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 10;
int c = 20;
if(a > b){
System.out.println("a 大于 b");
}
System.out.println("-------------");
if(a==b){
System.out.println("a 等于 b");
}
System.out.println("-------------");
if(a< c){
System.out.println("a 小于 c");
}
}
}
运行结果:
2. if双选择结构
-
语法:
if (条件表达式) {
// (如果条件表达式为true)将执行的语句
代码块1
} else {
// 条件表达式为false时将会执行的语句
代码块2
}
条件表达式结果为boolean类型,当表达式结果为true,则执行语句块A;否则执行语句块B。 if-else语句结构,根据需要可以嵌套使用。
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
if(a == b){
System.out.println("a 等于 b");
}else{
System.out.println("a 不等于 b");
}
}
}
运行结果:
3. if多选择结构
-
语法:
if (布尔表达式1) {
// (如果布尔表达式为true)将执行的语句
代码块1
} else if (布尔表达式2) {
// 如果上面判断为false且布尔表达式2为true将会执行的语句
代码块2
} else if (布尔表达式3) {
// 如果上面判断都为false且布尔表达式3为true将会执行的语句
代码块3
} else if (布尔表达式4) {
// 如果上面判断都为false且布尔表达式4为true将会执行的语句
代码块4
} else {
// 如果上面判断都为false将会执行的语句
代码块n
}
所有的条件表达式结果都是boolean类型。 如果条件表达式A结果为true,则执行语句块A,否则去判断条件表达式B; 如果条件表达式B结果为true,则执行语句块B,否则去判断条件表达式C; .... 如果所有的条件表达式结果都为false,则执行语句块N+1 else是可选的,根据需要可以省略
/**
* 成绩大于等于90时,评估结果为:优秀
* 成绩为(80-89)时,评估结果为:良好
* 绩为(60-79)时,评估结果为:中等
* 成绩小于60时,评估结果为:差
*/
public class Test01 {
public static void main(String[] args){
int score = 70; //考试成绩
if ( score >= 90 ) {
System.out.println("优秀");
} else if (score >= 80 ) {
System.out.println("良好");
} else if (score >= 60 ) {
System.out.println("中等");
} else {
System.out.println("差");
}
}
}
执行结果:
4. 嵌套的if结构
-
语法:
if(条件表达式1){ //先进的算法思想常用,如找出一个数,用二分法
if(布尔表达式2){
//如果条件表达式2的值为true,则执行该语句
代码块1
}else{
//如果条件表达式2的值为false,则执行该语句
代码块2
}
}else{
//如果条件表达式1的值为false,则执行该语句
代码块1
}
public class Test01 {
public static void main(String[] args){
int score = 70; //考试成绩
if ( score > 50 ) {
if(score >=70){
System.out.println("优秀");
}else {
System.out.println("及格");
}
} else {
System.out.println("不及格");
}
}
}
执行结果:
5. Switch多选择结构
-
语法
switch(变量){
case 值1:
//匹配则执行该语句
break; //结束
case 值2:
//匹配则执行该语句
break; //结束
...
default:
//都不匹配,执行该语句
break; //可省略,不建议
}
-
特点
-
switch语句中的变量类型可以为:byte,short,int,char,String字符串
-
选择判断固定值时用switch,判断区别或范围时则建议用if
-
case后面不加break会导致case穿透,某些时候巧妙运用可以简化代码
-
根据switch表达式中的值,依次匹配各个case中的常量。一旦匹配成功,则进入相应case结构中,调用其执行语句。当调用完执行语句以后,则仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或switch-case结构末尾结束为止。
default块顺序可以变动,但要注意其执行顺序。通常,default块放在末尾,也可以省略;但是如果default放在前面,推荐添加break; case的顺序无所谓,case 之后只能声明常量,不能声明范围。 程序会使用表达式值从上到下跟case后面的值进行比较,如果相等,则执行对应的语句块,否则继续向后对比; 如果都不相等,则执行default。
public class Test01 {
public static void main(String[] args) {
char grade = 'c';
//switch匹配一个值比较方便
switch (grade) {
case 'a':
System.out.println("优");
break;
case 'b':
System.out.println("良");
break;
//break在整个语句中,一旦被执行将会结束整个循环。
//如果不加break将会出现贯穿的现象。
//c和d如果不加break将会打印输出“中”后面的所有的输出字符。
case 'c':
System.out.println("中");
break;
case 'd':
System.out.println("差");
break;
//default的作用是在case匹配失败之后才会被执行,打印出来相对应的输出信息。
default:
System.out.println("您的输入有误!");
}
}
}
执行结果:
-
扩展
-
break关键字
-
break关键字可以用在switch中,一旦执行,整个switch将立刻结束。
-
break关键字可以用在switch中,一旦执行,整个switch将立刻结束。
-
-
default关键字
-
default的作用是在case匹配失败之后才会被执行,打印出来相对应的输出信息。
-
-
continue关键字
-
contine关键字立即跳过本次的循环,进入到下一个循环语句当中。
-
-
3. 循环结构
在某些条件满足的情况下,反复执行特定代码的功能 有while、do…while、for三种循环语句。 注:JDK1.5提供了foreach循环,方便的遍历集合、数组元素。
循环语句能够使程序代码重复执行,适用于需要重复一段代码直到满足特定条件为止的情况。
循环结构一般由四部分组成:
-
初始化
-
循环条件
-
循环体
-
迭代
而一个循环体结束的方式有两种:循环条件返回false、通过关键字break。
3.1 while循环
while也叫条件判断语句,它通过一个条件来决定内部的语句是否重复执行。
-
语法:
1.先定义迭代的变量
while(2. 条件表达式进行判断){ //如果表达式的值为true,则执行循环,否则推迟循环。如过恒为true,则循环进入死循环
3.循环体
4.迭代条件 //为了不进入死循环的状态,循环内需要不断改变表达式的条件,从而让循环有失效的时候产生(一定不要忘记)
}
执行过程: ①-②-③-④-②-③-④-②-③-④-...-②
解释: 条件表达式返回值类型为boolean类型,只要条件表达式的结果为true,则一直执行语句块。
特点:先判断,再执行
注意点:
-
循环开始前判断boolean表达式,如果boolean表达式一开始就为false,循环不会执行
-
只要Boolean表达式为true,循环就不会终止。
-
大多数情况我们需要让循环停下,因此我们需要一个让表达式失效的方式。
-
少部分情况需要循环一直执行,比如服务器的请求监听等。
-
循环条件恒为true就会造成无限循环,会影响我们的陈旭性能甚至造成卡死,应该尽量避免。
public class Test01 {
public static void main(String[] args) {
// 计算1+2+3+……+100=?
int i = 0;
int sum = 0;
while(i<=100){
sum = sum + i;
i++;
}
System.out.println("1+2+3+……+100="+sum);// 5050
}
}
while循环还有一种使用场景:在循环次数不确定的使用,可以将条件设定为true,在循环中进行条件的判断,然后利用关键字 break 跳出循环。
while(true){
循环体
if(判断条件){
break;
}
}
public class Test01 {
public static void main(String[] args) {
// 计算1+2+3+……+100=?
int i = 0;
int sum = 0;
while(true){
sum = sum + i;
i++;
if (i>100){
break;
}
}
System.out.println("1+2+3+……+100="+sum);// 5050
}
}
3.2 do…while循环
-
对于while语句而言,如果不满足条件,则不能进入循环体。但有时候我们需要即使不满足条件也至少执行一次
-
do..while循环和while循环很相似,不同的是。do…while至少执行一次
-
while和do..while的区别
-
while先判断后执行,do…while先执行后判断
-
do…while总是保证循环体会被至少执行一次!这是他们的主要区别
-
-
语法:
1.先定义迭代的变量
do{
3.循环体
4.迭代条件
}while(2.布尔表达式);
执行过程: ①-③-④-②-③-④-②-③-④-...②
解释: 先执行do对应的语句块,然后再判断循环条件,只要循环条件满足,则一直执行循环体,否则结束循环。 特点:先执行,再判断;do-while循环至少执行一次循环体。
//计算1+2+...+100的结果
public class Test01 {
public static void main(String[] args) {
int sum = 0;
int i = 0;
do {
sum += i;
i++;
}while (i<=100);
System.out.println("1+2+3+……+100="+sum);// 5050
}
}
3.3 for 循环
-
虽然所有循环结构都可以用while和do…while表示,但Java提供了另一种语句—>for循环,使一些循环结构变得更加简单
-
for循环语句是支持迭代的一种通用结构,是最有效最灵活的循环结构
-
for循环执行的次数是在执行前就确定了,语法结构如下:
for(1.初始化;2.布尔表达式;4.更新){
3.循环操作
}
for(;;){//死循环
}
执行过程: ①-②-③-④-②-③-④-②-③-④-.....-②
解释: 1、先执行初始化循环条件 2、使用初始表达式值进行条件判断,如果判断为true,则执行循环操作;否则结束循环 3、进行迭代 4、循环迭代可以在()里面省略,在循环体里面进行迭代也可。
public class Test01 {
public static void main(String[] args) {
//初始化;条件判断;迭代
for (int i = 1; i < 100; i+=2) {
System.out.println(i);
}
System.out.println("for循环结束");
}
}
执行结果:
【九九乘法表】
public class Test01 {
public static void main(String[] args) {
//打印九九乘法表
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j+"*"+i+"="+i*j+"\t");
}
System.out.println();
}
}
}
执行结果:
3.4 增强for循环(forEach)
-
java5引入了一种主要用于数组或集合的增强型for循环
-
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时的数组元素相等
-
表达式:表达式是要访问的数组名,或者是返回值为数组的方法
-
Java增强for循环语法格式如下:
for(类型 变量名 : 集合的变量名){//主要用于数组或集合的遍历。 //代码语句 }
public class Test01 {
public static void main(String[] args) {
int[] list = {10,20,30,40,50};//定义一个数组
for (int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
System.out.println("==========");
for (int x : list) {
System.out.println(x);
}
}
}
执行结果:
四、方法
1、方法的概述
Java方法是语句的集合,它们在一起执行一个功能。
方法是解决一类问题的步骤的有序组合
方法包含于类或对象中
方法在程序中被创建,在其他地方被引用
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展
-
方法的命名规则:第一个单词首字母小写,后跟驼峰命名法(即之后的单词首字母都大写)
2、方法的定义
-
ava的方法类似于其他语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:
-
方法包含一个方法头和一个方法体,下面是一个方法的所有部分:
-
修饰符:修饰符,这是可选的,告诉编译器如何调用该方法,定义了该方法的访问类型。
-
返回值类型:方法可能会返回值,returnValueType 是方法返回值的数据类型,有些方法执行所需的操作,但没有返回值,在这种情况下,returnValueType 是关键字void。
-
方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
-
参数类型:参数像是一个占位符,当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
-
形式参数:在方法被调用时用于接收外界输入的数据。
-
实参:调用方法时实际传给方法的数据。
-
-
方法体:方法体包含具体的语句,定义该方法的功能。
-
-
语法
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
1. 静态变量与静态方法
【修饰符】 class 类名{
【其他修饰符】 static 数据类型 变量名;//在方法之外
【其他修饰符】 static 返回值类型 方法名(【形参列表】){
方法体
}
}
public class 类名{
public static 数据类型 变量名; //静态变量
public static 返回值类型 方法名1(【形参列表】){//静态方法
方法体
}
public static 返回值类型 方法名2(【形参列表】){//静态方法
方法体
}
}
*其中static说明这是静态方法。静态成员和静态方法只能访问静态成员和方法,若是要*
*访问非静态成员或方法,则需要通过创建对象来访问,形如* ***类名 标识符=new 类名();***
*而非静态的成员既可以访问静态成员和方法也可以访问非静态成员和方法。*
public class Test01 {
public static void main(String[] args) {
hello();//在同一类下,静态成员之间的访问直接通过 方法名();
Test01.hello();
Test01.PI;
System.out.println(Test01.PI);
}
public static PI = 3.14;
public static void hello(){
System.out.println("好好学习,天天编程");
}
}
//访问另一个类的静态变量和静态方法
//类名.静态变量
//类名.静态方法
2. 非静态方法
public class Test01 {
public static void main(String[] args) {
Test01 test01=new Test01();//创建对象
test01.hello();//若静态方法想访问非静态方法,则得通过创建对象才能访问
}
public void hello(){//没有static表示非静态方法
System.out.println("好好学习,天天编程");
}
}
3. 修饰符访问权限范围
3、方法的重载
-
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
-
方法的重载的规则:
-
方法名称必须相同。
-
参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)。
-
方法的返回类型可以相同也可以不相同。
-
仅仅返回类型不同不足以成为方法的重载。
-
-
实现理论:
-
方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法,如果匹配失效,则编译器报错。
-
public class Test01 {
public static void main(String[] args) {
System.out.println(max(1,2));
System.out.println(max(2.3,2.5));
}
//int类型的max方法
public static int max(int a,int b){
return a>b?a:b;
}
//double类型的max方法
public static double max(double a,double b){
return a>b?a:b;
}
}
4、方法的参数传递
1. 基本数据类型的传递
所谓几本数据类型就是8大基本数据类型(byte char short int boolean float long double)
对于基本数据类型的传递,形参的改变是不会改变实参的值的
public class Test01 {
public static void main(String[] args) {
int a=10;
play(a);
System.out.println("实参的a的值是"+a);
}
public static void play(int a) {
a = 100;
System.out.println("形参中a的值是" + a);
}
}
运行结果:
2. 引用数据类型的传递
除了8大基本数据类型之外其余的是引用数据类型
引用数据类型的概念: 引用类型 就是只变量中存储的不是值而是一个内存中的地址的数据类型也就是说
变量中存储了这个变量的值所在内存中的地址 每次调用这个变量都是引用这个
地址而得到真正的值 所以叫引用类型
引用类型:
是一个对象类型,它的值是指向内存空间的引用,其实就是地址所指向的内存所保存的值
public class Test01 {
public static void main(String[] args) {
int[]arr={1,2,3,4};
System.out.println("调用前arr[0]:"+arr[0]);
arrDemo(arr);
System.out.println("调用后arr[0]:"+arr[0]);
}
public static void arrDemo(int[]arr){
arr[0]=10;
}
}
运行结果:
5、可变参数
-
JDK1.5 开始,Java支持传递同类型的可变参数给一个方法。
-
在方法声明中,在指定参数类型后加一个省略号(…)。
-
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
【修饰符】 class 类名{
【修饰符】 返回值类型 方法名(数据类型 参数名1, 数据类型... 参数名2){
方法体
}
}
【修饰符】 class 类名{
【修饰符】 返回值类型 方法名(数据类型... 参数名){
方法体
}
}
public class Test01 {
//声明一个方法,可以实现求任意个int整数的和。
public static int add(int... nums){// int... nums 可变参数,把nums当成数组
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
public static void main(String[] args) {
System.out.println(add(new int[]{1, 2, 3, 4, 5}));//传入数组对象给可变参数nums
/*int[] arr = {5,6,8,79,8};//声明和静态初始化在一个语句中完成,可以省略new int[],否则不可以省略。
System.out.println(add(arr));
int[] arr2 ;
// arr2 = {5,6,8,79,8};//报错
arr2 = new int[]{5,6,8,79,8};//报错*/
System.out.println(add(1, 2, 3, 4, 5));
System.out.println(add());//传入0个实参
System.out.println(add(100));//传入1个实参
System.out.println(add(100,200));//传入2个实参
}
}
6、 方法的递归
A方法调用B方法,我们很容易理解! 递归就是:A方法调用A方法!就是自己调用自己。
利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可以描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
递归结构包括两个部分:
递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
递归体:什么时候需要调用自身方法。
-
递归调用基本上都可以使用循环代替
-
递归调用的效率是比较低,但是通常递归调用的可读性更好
-
递归调用如果条件设置不当,会出现无限递归,(就相当于死循环)导致栈内存溢出
public class Test01 {
public static void main(String[] args) {
System.out.println(f(5));// 计算阶乘5(基数不可过大,否则程序会崩溃,电脑会卡死)
}
public static int f(int n){
if (n==1){
return 1;
}else {
return n*f(n-1);
}
}
}
五、数组
1、数组的定义
-
数组是相同类型数据的有序集合
-
数组描述的是相同类型的若干个数据,按照一定的先后顺序排列组合而成
-
其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们
2、数组声明创建
-
首先必须声明数组变量,才能再程序中使用数组.下面是声明数组变量的语法:
dataType[] arrayRefVar;//首选的方法或dataType arrayRefVar[];//效果相同,但不是首选方法
-
Java语言使用new操作符来创建数组,语法如下:
dataType[] arrayRefVar = new dataType[arraySize];
-
数组的元素是通过索引访问的,数组索引从0开始
-
获取数组长度
arrays.length
public class ArrayDemo01 {
//变量的类型 变量的名字 = 变量的值;
//数组类型
public static void main(String[] args) {
int[] nums;//1.声明数组
nums = new int[10];//创建数组
//3.给数组元素赋值
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
nums[5] = 6;
nums[6] = 7;
nums[7] = 8;
nums[8] = 9;
nums[9] = 10;
int sum = 0;
//计算所有元素的和
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
System.out.println(sum);
}
}
结果:
55
3、数组初始化
1. 静态初始化
-
格式:
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3…};
-
举例: 定义存储1,2,3,4,5整数的数组容器。
int[] arr = new int[]{1,2,3,4,5};
// 可以拆分
int[] arr;
arr = new int[]{1,2,3,4,5};
2. 动态初始化
-
格式:
数组存储的数据类型[ ] 数组名字 = new 数组存储的数据类型[数组长度]; 数组存储的数据类型 数组名字[ ] = new 数组存储的数据类型[数组长度];
-
数组定义格式详解:
-
数组存储的数据类型: 创建的数组容器可以存储什么数据类型。
-
[] : 表示数组。
-
数组名字:为定义的数组起个变量名,满足标识符规范,可以使用名字操作数组。
-
new:关键字,创建数组使用的关键字。
-
数组存储的数据类型: 创建的数组容器可以存储什么数据类型。
-
[长度]:数组的长度,表示数组容器中可以存储多少个元素。
-
注意:数组有定长特性,长度一旦指定,不可更改。
-
-
定义可以存储3个整数的数组容器,代码如下:
-
int[] arr = new int[3];
int arr[] = new int[3];
// 可以拆分
int[] arr;
arr = new int[3];
3. 数组的默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。
4. 扩展
Arrays.fill快速初始化,填充一个数组
java中的数组初始值都为零,若快速填充一个其他值的数组,即将数组批量填充相同的值,可以用 Arrays.fill 方法,但只能填充一个一维数组,多维数组还得用循环。
举例:
import java.util.Arrays;
public class HelloWorld {
public static void main(String[] args) {
int[] arr = new int[5];
Arrays.fill(arr, 1);
System.out.println(Arrays.toString(arr)); // [1, 1, 1, 1, 1]
}
}
5. 数组的四个基本特点
-
数组长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
-
数组元素必须是相同类型,不允许出现混合类型。
-
数组中的元素可以是任何数据类型,包括基本类型和引用类型。
-
数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
4、数组的访问
-
索引: 每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引 (index),可以通过数组的索引访问到数组中的元素。
-
格式:
数组名[索引]
-
数组的长度属性: 每个数组都具有长度,而且是固定的,Java中赋予了数组的一个属性,可以获取到数组的长度,语句为:数组名.length ,属性length的执行结果是数组的长度,int类型结果。由次可以推断出,数组的最大索引值为数组名.length-1。
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
//打印数组的属性,输出结果是5
System.out.println(arr.length);
}
-
索引访问数组中的元素:
-
数组名[索引] = 数值,为数组中的元素赋值
-
变量 = 数组名[索引],获取出数组中的元素
-
public static void main(String[] args) {
//定义存储int类型数组,赋值元素1,2,3,4,5
int[] arr = {1,2,3,4,5};
//为0索引元素赋值为6
arr[0] = 6;
//获取数组0索引上的元素
int i = arr[0];
System.out.println(i);
//直接输出数组0索引元素
System.out.println(arr[0]);
}
5、数组边界
-
下标的合法区间:[0,length-1],如果越界就会报错
public static void main(String[] args){
int[] a = new int[2];
System.out.println(a[2]);
}
-
java.lang.ArrayIndexOutOfBoundsException:数组下标越界异常
-
小结:
-
数组是相同的数据类型(数据类型可以为任意类型)的有序集合
-
数据也是对象。数组元素相当于对象的成员变量
-
数组长度是确定的,不可变的。如果越界,则报ArrayIndexOutOfBoundsException
-
6、多维数组
-
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的数组,其每一个元素都是一个一维数组
-
二维数组
int[][] array1 = new int[10][10];
int array2[][] = new int[10][10];
int array3[][] = { { 1, 1, 1 }, { 2, 2, 2 } };
int array4[][] = new int[][] { { 1, 1, 1 }, { 2, 2, 2 } };
7、Arrays类
-
Arrays类是数组的工具类 java.util.Arrays
-
由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
-
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而”不用”使用对象来调用(注意:是”不用”而不是“不能”)
7.1 输出数组 Arrays.toString()
int[] array = { 1, 2, 3 };
System.out.println(Arrays.toString(array));//[1, 2, 3]
7.2 数组转List Arrays.asList()
String[] array2 = {"a", "b", "c", "d"};
System.out.println(array2); //[Ljava.lang.String;@4554617c
List list = new ArrayList(Arrays.asList(array2));
System.out.println(list); // [a, b, c, d]
list.add("e");
System.out.println(list); // [a, b, c, d, e]
7.3 数组转Set Arrays.asList()
String[] array = { "a", "b", "c", "d", "e" ,"a"};
Set set = new HashSet(Arrays.asList(array));
System.out.println(set);//[a, b, c, d, e]
7.4 List转数组 toArray()
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
String[] array = new String[list.size()];
list.toArray(array);
for (String s : array)
System.out.println(s);
7.5 数组中是否包含某个值
String[] array = { "a", "b", "c", "d", "e" };
boolean isEle = Arrays.asList(array).contains("a");
System.out.println(isEle);//true
7.6 数组复制
int array[] = new int[] { 1, 2, 3, 4 };
int array1[] = new int[array.length];
System.arraycopy(array, 0, array1, 0, array.length);
System.out.println(Arrays.toString(array1));[1, 2, 3, 4]
7.7 数组排序
int[] a = {1,3,5,2,10,8,7,9};
Arrays.sort(a);
System.out.println(Arrays.toString(a));//[1, 2, 3, 5, 7, 8, 9, 10]
7.8 数组填充
int[] a = {1,3,5,2,10,8,7,9};
Arrays.fill(a,0);
System.out.println(Arrays.toString(a));//[0, 0, 0, 0, 0, 0, 0, 0]
Arrays.fill(a,2,4,0);//数组a的下标2和3的元素 填充为0
System.out.println(Arrays.toString(a));[1, 3, 0, 0, 10, 8, 7, 9]
8、冒泡排序
-
最基本的排序算法,两层循环,外层冒泡轮数,里层依次比较。由于是两层嵌套循环,他的时间复杂度为O(n^2)。以下是一个使用冒泡排序对数组进行排序的实例:
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {1, 4, 2, 5, 7, 2, 8};
bubbleSort(arr); //使用冒泡排序对数组进行排序
System.out.println(Arrays.toString(arr));//输出排序后的数组
}
//排序过程
//1 4 2 5 7 2 8
//1 2 4 5 2 7 8 (1) flag ** true
//1 2 4 2 5 7 8 (2) flag ** true
//1 2 2 4 5 7 8 (3) flag ** true
//1 2 2 4 5 7 8 (4) flag ** false
/**
* 冒泡排序,使用flag标签判断数组是否还需要排序,默认为false,即数组不需要再次排序,
* 如果在一次遍历中改变了元素的位置,即认为该数组仍然需要排序,将flag置为true。使用
* 这样的方法可以减少排序中数组有序时无用的循环遍历。
* 该算法的冒泡排序为从从小到大排序
* @param arr 待排序的数组,传递的为引用值
*/
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
boolean flag = false; //默认该次循环结束后不需要继续进行排序
for (int j = 0; j < arr.length-1; j++) {
if (arr[j] > arr[j+1]) {
int tmp = arr[j]; //交换元素位置
arr[j] = arr[j+1];
arr[j+1] = tmp;
flag = true; //更改标记flag
}
}
System.out.println(i+1 + ": " + Arrays.toString(arr));//输出每一次排序的结果
if (!flag) break;
}
}
}
六、面向对象
1、面向过程和面向对象
-
面向过程思想
-
步骤清晰简单,第一步做什么,第二步做什么
-
面对过程适合处理一些较为简单的问题
-
-
面向对象思想
-
物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索
-
面向对象适合处理复杂的问题,适合处理需要多人协作的问题
-
-
对于描述复杂的事物,为了宏观上把握、整体上合理分析,我们需要面向对象的思路来分析整个系统。但是,具体到微观才做,仍然需要面向过程的思想去处理。
1. 什么是面向对象
-
面向对象编程(Object-Oriented Programming,OOP)
-
面向对象的本质是:以类的方式组织代码,以对象的组织(封装)数据
-
抽象(抽出相似的部分)
-
三大特性:
-
封装
-
继承
-
多态
-
-
从认识论的角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
-
从代码运行的角度考虑是先有类,后有对象。类是对象的模板
2. 类与对象的关系
认识论:先有对象后有类
对象:是具体的事物 类:是抽象的,是对对象的抽象
代码运行角度:先有类后有对象
类是对象的模板
-
类是一种抽象的数据类型,它是对某一事物整体描述/定义,但是不能代表某一个具体的事物
-
对象是抽象概念的具体实例
3. 创建与初始化对象
-
使用new关键字创建对象
-
使用new关键字创建对象时,除了分配内存空间,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
-
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
-
必须和类名相同
-
没有返回值,也不能写void
-
2、构造方法
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的,并且构造器有以下2个特点:
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#981a1a">-</span> <span style="color:#000000">必须和类的名字相同</span>
<span style="color:#981a1a">-</span> <span style="color:#000000">必须没有返回类型也不能写void</span>
<span style="color:#981a1a">-</span> <span style="color:#000000">一个类即使什么都不写,默认也包含一个不太参数的构造方法</span>
<span style="color:#981a1a">-</span> <span style="color:#000000">定义了有参的构造方法后,如果想使用无参构造方法,必须显示声明</span>
<span style="color:#981a1a">-</span> <span style="color:#000000">使用new关键字,本质是调用构造器来初始化值</span>
<span style="color:#981a1a">-</span> <span style="color:#000000">属性:字段Field</span> <span style="color:#000000">成员变量</span>
<span style="color:#981a1a">-</span> <span style="color:#000000">默认初始化:</span>
<span style="color:#981a1a">-</span> <span style="color:#000000">数字</span> <span style="color:#116644">0</span> <span style="color:#116644">0.0</span>
<span style="color:#981a1a">-</span> <span style="color:#008855">char</span>: <span style="color:#000000">u0000</span>
<span style="color:#981a1a">-</span> <span style="color:#008855">boolean</span>: <span style="color:#221199">false</span>
<span style="color:#981a1a">-</span> <span style="color:#000000">引用:null</span></span></span>
-
构造方法:
-
和类名相同
-
没有返回值
-
-
作用:
-
new本质是调用构造器
-
初始化对象的值
-
-
注意点:
-
定义有参构造之后,如果想使用无参构造,显式定义一个无参构造
-
-
语法:
public 类名(){ //idea自动生成 alt + insert }
public class Test01 {
public static void main(String[] args) {
//创建对象时可以直接调用类的有参构造方法,直接把对象的属性值传递
Person person = new Person("张三");
System.out.println(person.name);
}
}
class Person{
//一个类即使什么都不写,它都会存在一个方法
String name;
//显式的定义构造器
//实例化初始值
//1.使用new关键字,本质是在调用构造器
public Person(){
}
//有参构造:一旦定义了有参构造 无参构造就必须显式定义
public Person(String name){
super();//每次执行之前会调用父类的构造方法 默认和有super();任何一个类的构造方法首次执行的都是super()
this.name = name;
}
}
3、 面向对象三大特性
1. 封装
我们程序设计要追求“高内聚,低耦合”。高内聚:就是类的内部数据操作细节自己完成,不允许外部干涉,低耦合:仅暴露少量的方法给外部使用。
-
封装是将内部信息隐藏,禁止直接访问一个对象中数据,对外暴露接口让使用者去调用
-
封装的关键
-
属性私有(private)
-
get/set方法(alt+insert快速生成)
-
-
封装的意义:
-
提高程序的安全性,保护数据
-
隐藏代码的实现细节
-
统一接口
-
统一接口
-
public class Teacher {
//private修饰的变量作用域仅在本类
//通过private修饰 隐藏属性
private String name;
private int age;
//通过get/set方法让其他类调用访问本类属性
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public class Test01 {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setName("张三");
System.out.println(teacher.getName());
}
}
2. 继承
-
继承的本质是对某一批类的抽象
-
继承的关键字是
extends
-
Java中只有单继承,没有多继承
-
继承是类与类之间的关系,父类(基类),子类(派生类)
-
子类和父类之间是有’is a’的关系
//Person 人 : 父类(基类)
public class Person /*extends Object*/{
public int money = 10000;
public void say(){
System.out.println("一个人");
}
}
//Student 学生 is a 人 :子类(派生类)
public class Student extends Person{
//仅有默认无参构造方法
//但因为继承了Person则继承了父类的所有方法和属性
}
public class Test01 {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.money);
student.say();
}
}
执行结果:
解释】
Student extends Person
Student类继承了Person类,虽然Student中没有写任何代码,但是Student继承了Person类中的属性以及方法。
通过看类之间的关系发现(ctrl+h),任何类都会默认继承Object类(Java.lang.Object)
2.1 super和this
-
super
-
super调用父类的构造方法,必须在构造方法的第一行出现
-
super必须只能出现在子类的方法或者构造方法中
-
super和this不能同时调用构造方法
-
-
this
-
代表对象不同
-
this:本身调用该类的对象
-
super:代表父类对象的应用
-
-
前提
-
this:没有继承也可以使用
-
super:只要在继承条件下才可以使用
-
-
构造方法
-
this():本类的构造
-
super(): 父类的构造
-
-
public class Person {
public String name = "张三";
public Person(){
System.out.println("Person的无参构造");
}
}
public class Student extends Person {
public String name = "李四";
public Student(){
//隐藏代码:调用父类构造器
//super();这语句必须在子类的构造器的第一行
System.out.println("Student无参构造");
}
public void test(String name){
System.out.println(name);
//this代指调用的对象(本类对象)
System.out.println(this.name);
//super指代父类
System.out.println(super.name);
}
}
public class Test01 {
public static void main(String[] args) {
//new Student()时会自动调用父类无参构造
Student student = new Student();
student.test("魏武");
}
}
3. 重写
-
重写是方法的重写
-
需要有继承的关系,子类重写父类的方法
-
方法名必须相同
-
参数列表必须相同
-
修饰符:范围可以扩大但不能缩小 (public > protected > default > private)
-
抛出的异常:可以缩小但是不能扩大 ClassNotFoundException —> Exception
-
重写,子类的方法和父类的方法必须一致,方法体不同。
-
返回值可以一致,也可以是父类返回值的子类
-
父类的功能,子类不一定需要,或者不一定满足,所以子类需要重写父类的方法
public class Person {
public void run(){
System.out.println("Person中的run");
}
}
public class Student extends Person {
public void run(){
System.out.println("Student中的run");
}
}
public class Test01 {
public static void main(String[] args) {
//方法的调用和左边有关,定义的数据类型有关
Student s1 = new Student();
Person s2 = new Person();//子类的方法重写了父类的方法
s1.run();//Student中的run
s2.run();//Person中的run
}
}
4. final
final: 不可改变,最终的含义。可以用于修饰类、方法和变量。
-
类:被修饰的类,不能被继承。
-
方法:被修饰的方法,不能被重写。
-
变量:被修饰的变量,有且仅能被赋值一次。
5. 多态
-
即统一方法可以根据发送对象的不同而采取多种不同的方式
-
一个对象的实际类型是确定的,但是可以指向对象的引用类型有很多
-
多态存在的条件
-
有继承关系
-
子类重写父类的方法
-
父类的引用指向子类对象
-
对象能执行那些方法,主要看对象左边的类型
-
-
多态是方法的多态,属性没有多态
-
父类和子类有关系,类型转换异常
-
存在条件:继承关系,方法需要重写,父类引用指向子类对象
-
static不能被重写,属于类,不属于实例
-
final:常量 无法重写
-
private方法
-
public class Person {
public void run(){
System.out.println("Person中的run");
}
}
public class Student extends Person {
public void run(){
System.out.println("Student中的run");
}
public void eat(){
System.out.println("Student中的eat");
}
}
public class Test01 {
public static void main(String[] args) {
/*
* 一个对象的实际类型是确定的
* new Student();
new Person();
*/
//student可以调用的方法都是自己的或者是父类的
Student s1 = new Student();
//person父类型只能调用自己的方法,不能调用子类特有的方法
Person s2 = new Student();
s1.run();
s2.run();//子类重写了父类的方法,执行子类的方法
((Student)s2).eat();//若父类型调用子类中独特方法需要强制转换
}
}
执行结果:
6. instanceOf
instanceof是Java的一个二元操作符,和==,>,<是同一类东东。由于它是由字母组成的,所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。
Student stu = new Student();
if(stu instanceOf Object){
System.out.println("stu属于Person");
}
在比较一个对象是否和另一个对象属于同一个类实例的时候,我们通常可以采用instanceof和getClass两种方法通过两者是否相等来判断,但是两者在判断上面是有差别的。Instanceof进行类型检查规则是:你属于该类吗?或者你属于该类的派生类吗?而通过getClass获得类型信息采用==来进行检查是否相等的操作是严格的判断,不会存在继承方面的考虑;
总结:在写程序的时候,如果要进行类型转换,我们最好使用instanceof运算符来判断它左边的对象是否是它右边的类的实例,再进行强制转换。
7. static关键字
-
表示静态的,相当于中文中的形容词
-
static静态变量在类中是共享的
-
非静态方法可以访问本类中的所有静态方法
-
静态方法可以调用本类中所有的静态方法
public class Test1 {
private static int name;//静态变量
private double age;//非静态变量
public static void main(String[] args) {
System.out.println(name);//直接可以调用
System.out.println(age);//不可以直接调用
Test1 t1 = new Test1();
//通过对象调用
System.out.println(t1.age);
System.out.println(t1.name);
}
}
静态方法和变量是和main方法一起被加载出来的,非静态方法和变量此时还没有被加载
8. 抽象类
-
被abstract关键字修饰的类称为抽象类
-
抽象类中的方法是没有方法体的(方法名后面是() 没有{} )
-
被abstract修饰并且没有方法体的方法称为抽象方法
-
抽象方法必须在抽象类中,抽象类中的方法不一定都是抽象方法
-
抽象类是不能创建对象的,必须通过子类继承抽象类,并且重写和实现里面的抽象方法,
-
语法:
//必须通过子类继承抽象类,并且重写和实现里面的抽象方法 访问修饰符 abstract class 类名{ //没有方法体 访问修饰符 abstract 返回值类型 方法名(); }
//抽象类
public abstract class Person {
//约束 有人帮我们实现
//只有方法的名字,没有方法的实现
//抽象方法必须在抽象类中
public abstract void eat();
}
//抽象类的所有方法,继承了它的子类,都必须要实现它的方法,除非子类本身也是一个抽象类。
public class Student extends Person{
@Override
public void eat() {
System.out.println("吃饱饱");
}
}
9. 接口
-
接口的定义 : 用interface修饰 接口都需要实现类
-
接口中的所有定义都是抽象的,默认情况 下都是用 public abstract 修饰 的
-
接口都需要有实现类 实现接口 用implements 关键字
-
一个类可以实现多个接口,一个类实现接口,必须重写接口中的所以方法
//接口定义的关键字interface
public interface userService {
int i = 10;//接口中的属性默认都是 public static final 修饰的
//接口中的所有定义都是抽象的 默认情况 下都是用 public abstract 修饰 的
void add(String name);
void delete(String name);
}
public interface kehuService {
void query();
}
//接口都需要有实现类 实现接口 implements
//一个类可以实现多个接口,一个类实现接口,必须重写接口中的所以方法
public class userServiceImpl implements userService,kehuService{
@Override
public void add(String name) {
System.out.println("添加的方法");
}
@Override
public void delete(String name) {
System.out.println("删除的方法");
}
@Override
public void query() {
System.out.println("查询的方法");
}
}
10. 内部类
-
将一个类定义在一个类或者一个方法里面,这样的类称着内部类。
-
一个Java文件中可以有多个类,但是只能有一个public修饰的类,并且这个类的类名要和文件的名字保持一致。
-
内部类的分类:
-
成员内部类
-
静态内部类
-
匿名内部类
-
局部内部类
-
10.1 成员内部类
-
定义: 定义:成员内部类是Java中最普通的一种内部类,成员内部类可以访问外部类所有的属性和方法。但是外部类要访问成员内部类的属性和方法,必须要先创建对象,通过对象访问成员内部类的属性和方法
public class Test01 {
public static void main(String[] args) {
A a = new A();
a.eat();
//通过这个外部类对象来创建内部类对象
A.B ab = a.new B();
ab.run();
ab.getName();
}
}
class A{
private String name = "张三";
public void eat(){
System.out.println("A类中的方法");
}
class B{
public void run(){
System.out.println("B类中的run方法");
}
public void getName(){
//成员内部类可以访问外部类所有的属性和方法
System.out.println("A类中的私有属性" + name);
eat();
}
}
}
10.2 静态内部类
-
静态内部类就是在成员内部类多加了一个 static 关键字。静态内部类只能访问外部类的静态成员变量和方法
-
可以直接访问外部类的所有静态成员,包含私有的,但不能访问非静态成员;
-
可以添加任意的修饰符,public、private、默认、protected,
-
因为静态内部类的地位就是外部类的一个成员;
-
作用域 : 与外部类的其他成员一致,是整个的类体;
-
静态内部类 访问 外部类 的【静态】属性/方法 : 直接访问即可;
-
外部类 访问 静态内部类 : 通过创建对象再访问的方式;
-
外部其他类 访问 静态内部类 :
-
方式一 : 直接通过外部类获取成员内部类对象;
-
方式二 : 通过外部类的方法,获取一个成员内部类的对象。
-
外部类 和 静态内部类的 成员重名时,静态内部类中访问重名的成员,默认遵循就近原则,
-
如果 静态内部类 就是想访问 外部类的重名成员,则使用语法 : 【外部类名.静态成员名称】
public class Test01 {
public static void main(String[] args) {
A a = new A();
a.eat();
//通过这个外部类来创建内部类对象
A.B ab = new A.B();
ab.run();
ab.getName();//成员内部类可以访问外部类所有的属性和方法
}
}
class A{
private String name = "张三";
public static void eat(){
System.out.println("A类中的方法");
}
static class B{
public void run(){
System.out.println("B类中的run方法");
}
public void getName(){
// System.out.println("A类中的私有属性" + name);
//只能访问外部类中的静态方法
eat();
}
}
}
10.3 匿名内部类
-
就是没有名字的内部类
public class Test01 {
public static void main(String[] args) {
//如果我们只是想单纯的使用一次eat方法,不需要创建对象的话.此时,便用到了匿名内部类
new A(){
@Override
public void eat() {
System.out.println("正在调用eat方法");
}
}.eat();
}
}
interface A{
public void eat();
}
10.4 局部内部类
-
又称“方法内部类”,就是定义在某个局部范围中的类,它和局部变量一样,都是在方法中定义的,其有效范围只限于方法内部。 在局部内部类中,局部内部类可以访问外部类的所有成员变量和方法,而局部内部类中的变量和方法却只能在创建该局部内部类的方法中进行访问。