栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JavaSE

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JavaSE

第一章 Java概述 1、Java的历史

Java目前已经是Java17了,但是我们学习的和用的仍然是Java8。

Java是95(96)正式对外发布。

Java诞生于SUN公司,现在归Oracle(甲骨文)。

Java之父:詹姆斯.高斯林。

2、Java的特点

面向对象、跨平台(JVM:Java虚拟机)、健壮、安全、支持分布式等。

3、搭建环境

JDK = JRE + 开发工具

JRE = JVM + 核心类库

JVM:Java虚拟机,只识别字节码格式数据,负责把字节码解释成CPU能识别的指令等。JVM有内存管理机制,有自己的垃圾回收(GC)机制。

JRE:Java运行环境。

JDK:Java开发工具包。

开发人员:安装JDK。

如果希望在任意目录下都可以使用javac,java等开发工具,最好配置环境变量。

JAVA_HOME=JDK的安装根目录

path新建一个变量值%JAVA_HOME%bin

4、HelloWorld程序
//class是关键字,表示定义一个类
//HelloWorld是类名,自己命名的
class HelloWorld{
    //main方法,主方法,是Java程序的入口,固定写法
	public static void main(String[] args){
        //语句,语句以;结尾,所有标点符号是英文半角输入
    	System.out.println("java");
    }
}
第二章 Java的基础语法 2.1 注释
单行注释:
    //
多行注释:
    
文档注释:
    
2.2 关键字

一共50个单词,全部是小写的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIQEcz9w-1647162051490)(复习笔记.assets/image-20220301093011253.png)]

昨天已经见过的:

class,public,static,void,byte,short,int,long,float,double,char,boolean

以下这些不是关键字:

main,String,System,out,println,java都不是关键字

两个保留字(在关键字表中):

const,goto

3个特殊值(不在关键字表中):

true,false,null
2.3 标识符
1、标识符的命名规则和规范(各5条)
命名规则:
(1)标识符可以由26个英文字母大小写、数字0-9、下划线_、美元符号$
(2)数字不能开头
(3)不能直接使用关键字、保留字、特殊值
class不能直接做标识符,但是myClass是可以。
(4)标识符中间不能包含空格
(5)严格区分大小写
命名规范:
(1)见名知意
(2)变量名等,从第二个单词开始首字母大写,例如:xxxYyyZzz
(3)类名等,每一个单词首字母大写,例如:XxxYyyZzz
(4)常量名,每一个单词都大写,下划线分割多个单词,例如:XXX_YYY_ZZZ
(5)包名,每一个单词都小写,.分割多个单词,例如:xxx.yyy.zzz
不能使用java开头取包名。
2.4 Java的数据类型
分两大类:
(1)基本数据类型:
byte,short,int,long,float,double,char,boolean
(2)引用数据类型:
类、数组、接口、枚举等
2.5 字面常量
long类型,当整数值比较大,需要在数字后面加L或l(小写)
float类型,需要在小数后面加F或f
double类型,可以在数字后面加D或d,也可以省略D或d
char类型,需要使用单引号
String类型,需要双引号
2.6 变量
变量的三要素:数据类型、变量名、变量值
使用要求:
(1)先声明后使用
(2)声明后还需要初始化
(3)变量有作用域
(4)同一个作用域中不允许重复声明,但是可以反复赋值
(5)变量的值的类型必须和变量声明的类型一致或兼容

变量的声明:

数据类型 变量名;

变量的赋值:

变量名 = 值;
2.7 最终变量
final 数据类型 变量名 = 值;

这种变量的值是不允许修改的。

这种变量称为常量,常量名一般是大写的。

2.8 计算机如何存储数据 2.8.1 进制分类和表示

进制分类:二进制、八进制、十进制、十六进制

二进制:以0B开头,可以是0B或0b,习惯用0B。数字范围是0和1

八进制:以0开头。数字范围是0和7。

十进制:正常表示。数字范围是0-9。

十六进制:0X,可以是0X或0x,习惯用0X。数字范围是0-9,A-F或a-f。

2.8.2 数据存储单位和方式

最小单位:bit比特,1位。

最基本单位:byte字节,8位。

符号位:看二进制的最高位(最左边),1表示负数,0表示正数。

原码、反码、补码:

正数:原码、反码、补码三码合一负数:原码、反码、补码三码不同

-25 + 30
    
-25原码:10000000 00000000 00000000 00011001
-25反码:11111111 11111111 11111111 11100110  在原码基础上,符号位不变,其余1变0,0变1
-25补码:11111111 11111111 11111111 11100111  在反码基础上,+1
    
    
+30补码:00000000 00000000 00000000 00011110  原码、反码、补码一致
    
-25补码:11111111 11111111 11111111 11100111
+30补码:00000000 00000000 00000000 00011110 
-25+30:00000000 00000000 00000000 00000101  ==>5

2.8.3 各种数据类型宽度和存储方式
byte:1个字节,范围-128~127  其中-128比较特殊,理解为特殊规定 1000 0000,其他的数字都可以通过原码、反码、补码的概念推算。
short:2个字节,范围-32768~32767
int:4个字节
long:8个字节
float:4个字节
double:8个字节
char:2个字节(在Java程序的内存中)Unicode字符集的编码值(它也兼容ASCII码表)
boolean:1位

注意:

(1)float和double

其中float和double类型的存储是分为(1)符号位(2)指数位(3)尾数位的三个部分存储。

就因为它存储指数,所以float类型的4个字节反而比long类型的8个字节的数据范围还大。

因为double类型的指数位和尾数位都比float类型多,所以double类型的数据范围和精度都比float要大。

float类型大概是小数点后7-8位(基于科学计数法)

double类型大概是小数点后15-16位(基于科学计数法)

因为float和double的二进制和十进制转换之间会有约等于的情况,不精确的。

(2)char

char类型底层二进制是通过char类型对应的编码值计算出来的。

每一个字符都有自己对应的唯一的编码值。

例如:

'a'的编码值97
'b'的编码值98
    ...
'A'的编码值是65
'B'的编码值是66
    ...
'0'的编码值是48
'1'的编码值是49
    ...
    

char类型在程序中可以用以下几种方式表示:

(1)'单个字符'
(2)直接用十进制的编码值
    char c = 97;  等价于 char c = 'a';
(3)使用十六进制编码值
    char c = 'u0061'; 十六进制,1位十六进制=4位二进制,4位十六进制,等价于16位二进制,2个字节
(4)有几个特殊字符需要转义
  \   如果要表示斜杆,无论在单引号还是双引号里面都要转义
  '   如果要表示单引号,在单引号里面需要转换,在双引号中不用转义 "'"
  "   如果要表示双引号,在双引号里面需要转换,在单引号中不用转义'"'
  t   制表符
  r   回车结束本行,光标到行首
  n   换行,结束本行,光标到下一行
  b   回退一位

2.9 基本数据类型转换

1、基本数据类型之间的转换(boolean类型不参与)

(1)自动类型转换

A:多种类型混合运算,换算为它们中最大大

B:byte和byte,short和short,char和char,或是它们三个之间计算都是转为int处理

C:当把存储范围小的数据类型的数据 赋值给 存储范围大的类型的变量时,自动类型提升

byte->short->int->long->float->double

​ char->

(2)强制类型转换

A:当把存储范围大的数据类型的数据 赋值给 存储范围小的类型的变量时,强制类型转换,这种有风险,可能会溢出或损失精度

B:当需要提升某个变量或表达式结果的类型,没有风险。

(转换后的类型)变量		转换某一个变量
(转换后的类型)(表达式)   转换的是表达式计算的结果  
    
int x = 1;
int y = 2;
System.out.println((double)x/y); //转x的类型为double
System.out.println((double)(x/y));//转x/y的结果的类型为double

2、基本数据类型和字符串之间的转换

无论什么类型只要和字符串进行“+”拼接,结果都变成了字符串。

2.10 运算符 2.10.1 运算符的分类

一共38个运算符。

按照操作数的个数分:

一元运算符(单目运算符):++,--,逻辑非,按位取反,正号(+),负号(-)
二元运算符(双目运算符):其余都是二元的
三元运算符(三目运算符): ? :

按照功能分:

算术运算符、比较运算符、逻辑运算符、条件运算符、位运算符、赋值运算符、Lambda操作符。

2.10.2 算术运算符
加:+
   (1)当+左右两边都是数值类型或char,表示求和
   (2)当+左右两边只要出现字符串,表示拼接
   (3)当+作为一元运算符使用时,表示正号,而且+要在变量或数字的前面
减:-
    (1)当-左右两边都是数值类型或char,表示求差
    (2)当-作为一元运算符使用时,表示负号,而且-要在变量或数字的前面
 乘:*
 除:/
    (1)如果两个整数相除,结果只保留整数部分
    (2)如果两个整数相除,除数为0,编译通过,运行报错 ArithmeticException算术异常
    (3)如果两个小数相除,除数为0,编译通过,运行结果是Infinity
 模:%
    (1)无论是整数还是小数都可以求模
    (2)模运算结果的正负号看被模数
 自增:++
 自减:--
    (1)当自增/自减表达式单独加;成一个语句时,++/--在前在后没影响
        a++;
		++a;
		a--;
		--a;
	(2)当自增/自减表达式和其他的运算一起构成一个语句时,++/--在前在后是不同。
        ++/--在前:先自增/自减,再取自增变量的值
        ++/--在后:先取自增/自减变量原来的值,然后自增/自减变量+/-1,然后用已经取出来的值做其他运算

2.10.3 比较运算符
大于:>
小于:<
大于等于:>=
小于等于:<=
等于:==   特别容易和=混淆
不等于:!=

    
特点:
    所有的比较运算符都是二元运算符,计算的结果都是true/false

2.10.4 逻辑运算符
逻辑与:&
短路与:&&(开发中一般用它)
    无论是&还是&&,都是两边为true的时候,最终结果才为true。
    其中&&的左边为false时,右边会不看。
逻辑或:|
短路或:||
    无论是|还是||,都是两边只要有一边是true,结果就为true。
    其中||的左边为true时,右边会不看。
逻辑异或:^
    只有两边不同时,一边为true,另一边为false,结果才为true。
逻辑非:!
    !true为false
    !false为true
    
特点:
    逻辑运算符的操作数一定是boolean值,结果也是boolean值
2.10.5 位运算符
左移:<<
    快速口诀:左移几位,就是乘以2的几次方
右移:>>
    快速口诀:右移几位,就是除以2的几次方,向下取整
    无论是左移还是右移,当移动位数超过当前类型的总宽度时,会先减去总宽度。
    int a = 5;
	a << 40 等价于 a << (40-32)
无符号右移:>>>
    正数和普通右移一样。
    负数不同,最高位补0,会使得负数变为正数。
        
    只有负数的普通右移,会在最左边补1,其他移位都是补0。
按位与:&
    对应二进制位都是1时,这个二进制位才为1。
按位或:|
    对应二进制位只要有1,这个二进制位就为1。    
按位异或:^
    对应二进制位只有一个是1,一个是0, 这个二进制位才为1。
按位取反:~
    原来0的变为1,1的变为0,包括符号位。
2.10.6 条件运算符
条件表达式 ? 结果表达式1 : 结果表达式2
    
注意:
    三元运算符的表达式不能直接加;构成语句,需要把结果赋值给变量,或者直接输出结果等,才可以。
    即 
    条件表达式 ? 结果表达式1 : 结果表达式2; 错误的
    变量 = 条件表达式 ? 结果表达式1 : 结果表达式2;
    System.out.println(条件表达式 ? 结果表达式1 : 结果表达式2);

2.10.7 赋值运算符
(1)基本的赋值运算符  =
(2)扩展的(复合的)赋值运算符: +=,-=,*=,/=,%=,<<=,>>=,>>>=,&=,|=,^=
 
注意:
    赋值运算符优先级一定是最低的,一定是最后算赋值操作。
    赋值运算符的左边一定是变量,不能是表达式、常量等。
    如果是=,要求=右边的值(常量值、变量值、表达式的值)的类型 小于等于 =左边变量的类型。如果大于了左边变量的类型,一定要做强制类型转换。
    如果是扩展的赋值运算符,当=右边的值(常量值、变量值、表达式的值)的类型大于左边变量的类型时,会自动发生强制类型转换。    

2.10.8 优先级

(1)单目:++,–,~,!

(2)算术运算符:先乘除模,再加减

(3)移位:<<,>>,>>>

(4)比较运算符:先大小比较>,<,>=,<=,再等或不等比较==,!=

(5)逻辑运算符:依次是&,^,|,&&,||

(6)条件运算符:? :

(7)赋值运算符:所有赋值运算符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6L9CW5m8-1647162051492)(复习笔记.assets/1553858424335.png)]

第三章 流程控制语句结构 3.1 表达式和语句

表达式分三种:

(1)计算表达式:常量值、变量值 加上 运算符

(2)new表达式

new Scanner(System.in)

(3)方法调用表达式

System.out.println()
input.nextInt()
input.next()

语句分为两大类:

(1)单语句

; 空语句

表达式直接加;构成的语句
    (1)自增、自减、赋值表达式可以直接;构成语句
    (2)new表达式可以直接;构成语句
    (3)方法调用表达式可以直接;构成语句
        如果是(2)和(3),没有把表达式结果赋值给变量,结果就会丢失。
        
       new Scanner(System.in);  这个语句创建的对象就丢失了
       正确的写法  Scanner input = new Scanner(System.in);

		input.nextInt();//从键盘接收的整数就丢失了;
		正确的写法:int num = input.nextInt();

(2)复合语句

条件判断,选择结构,循环结构等等

3.2 流程控制语句结构

1、流程控制语句结构有三种

(1)顺序结构:Java代码方法体中的语句整体都是从上往下执行的

(2)分支结构:

条件判断:if…else选择结构:switch…case

(3)循环结构

3.3 条件判断(必须掌握) 3.3.1 单分支条件判断
if(条件表达式){
    语句块;
}

执行特点:

当if()中结果是true时,就执行对应的{}中的语句块,否则就不执行。

注意:

(1)if()中的值(变量、常量、表达式)一定要是boolean类型
(2){}中如果只有一个语句,{}可以省略。
    千万不要在if()后面直接写;

3.3.2 双分支条件判断
if(条件表达式){
    语句块1;
}else{
    语句块2;
}

执行特点:

当if()中结果是true时,就执行对应的{}中的语句块1,否则就执行语句块2。

注意:

(1)if()中的值(变量、常量、表达式)一定要是boolean类型
(2){}中如果只有一个语句,{}可以省略。
    千万不要在if()后面直接写;
(3)else不能单独使用,必须配合if使用

3.3.3 多分支条件判断
if(条件表达式1){
    语句块1;
}else if(条件表达式2){
    语句块2;
}else if(条件表达式2){
    语句块2;
}...
【else{
     语句块n+1;
 }】

 说明:笔记中【】表示这个部分可选,有可能有,有可能没有

执行特点:

(1)先判断if(条件表达式1),如果它成立,就直接执行语句块1,后面的所有条件和分支都不看了;
(2)如果if(条件表达式1)不成立,接着看if(条件表达式2),如果它成立,就直接执行语句块2,后面的所有条件和分支都不看了;
 以此类推
 总之,下面的条件要被看到,说明上面的条件不成立。
 
 如果所有的if都不成立,那么就找最下来的else分支执行。

注意:

(1)if()中的值(变量、常量、表达式)一定要是boolean类型
(2){}中如果只有一个语句,{}可以省略。
    千万不要在if()后面直接写;
(3)else不能单独使用,必须配合if使用
(4)多个if()和else if()的条件范围是有重叠部分(包含关系),必须小的在上,大的在下面
    多个if()和else if()的条件范围是没有重叠部分(互斥关系),顺序随意

3.3.4 嵌套
嵌套的原则是:外层的满足后,再看里面的。外层的不满足,里面的是不看的。

3.4 选择结构
switch(表达式){
    case 常量值1:
        语句块1;
        【break;】
    case 常量值2:
        语句块2;
        【break;】  
    ...
    【default:
        语句块n+1;
        【break;】  
}

执行特点:

(1)入口
    A:switch()中表达式的值和case后面的常量值匹配了,就从这个case进入执行
    B:switch()中表达式的值和所有case后面的常量值都不匹配,就从default进入。
    无论defaut编写的位置在哪里,都是先看A,如果A没找到,再看B;

    
(2)贯穿/穿透
    一旦找到入口,就会一直贯穿执行,除非遇到出口。
    
 (2)出口
    A:自然出口:switch的结束}
    B:中断出口:break,return等

注意:

(1)case后面必须写常量值或常量表达式,不能是变量
(2)case后面的常量值不能重复,即不能有两个case后面的常量值相同
(3)switch()中值(变量、常量、表达式)的类型只能是
    byte,short,int,char四种基本数据类型
    和
    枚举和String两种引用数据类型
3.5 循环结构 3.5.1 for循环

1、语法结构

for(;;){
    循环体语句块;
}

for(【循环变量的初始化】; 【循环条件表达式】; 【迭代表达式】){
    【循环体语句块;】
}

2、执行特点

(1)先执行【循环变量的初始化】

(2)判断【循环条件表达式】结果:

如果为true,那么执行(3)

如果为false,直接结束当前for

(3)执行【循环体语句块;】

(4)执行【迭代表达式】

(5)回到(2)

3、注意

(1)for()中的两个;不能多也不能少

(2)如果循环条件第一次就不成立,那么循环体语句就一次也不执行。

(3)for(;{}结构是死循环,除非在{}中有break、return等语句,否则无法结束循环。

3.5.2 while循环

1、语法结构

while(循环条件表达式){
    【循环体语句块;】
}

while(true){
    【循环体语句块;】
}

2、执行特点

(1)先判断“循环条件表达式”

如果为true,那么执行(2)

如果为false,直接结束当前while

(2)执行【循环体语句块;】

(3)回到(1)

3、注意

(1)while()中的条件不允许省略

(2)while循环也可能第一次判断条件就不成立,循环体语句块一次也不执行。

(3)如果while(true)就是死循环,除非{}中有break,return等语句,否则就无法结束循环。

3.5.3 do…while循环

1、语法结构

do{
    【循环体语句块;】
}while(循环条件表达式);

do{
    【循环体语句块;】
}while(true);

2、执行特点

(1)先执行一次【循环体语句块;】

(2)先判断“循环条件表达式”。循环条件成立,继续循环,循环条件不成立,结束循环。

如果为true,那么执行(3)

如果为false,直接结束当前do…while

(3)执行【循环体语句块;】

(4)回到(2)

3、注意

(1)do…while()中的条件不允许省略

(2)do…while循环至少执行一次循环体语句。

(3)如果do…while(true)就是死循环,除非{}中有break,return等语句,否则就无法结束循环。

3.5.4 三种循环的对比

注意三种循环都可以实现重复执行xx代码的功能,完全可以互换。

习惯上:for更多的用于的循环次数或循环的起点和终点比较明显的场景。

while习惯用于循环次数不明显,循环条件比较明显的场景。

do…while习惯用于循环体至少执行一次的场景。

3.5.5 跳转语句 1、break

break必须出现在:

(1)switch

(2)循环中(for,while,do…while)

如果同时出现在switch和循环的嵌套代码中,跳出最近的。

package review;

public class TestBreak {
    public static void main(String[] args) {
   //     int age = 18;



        for(int i=1; i<=3; i++){
            switch (i){
                case 1:
                    System.out.println("monday");
                    break;//这里跳出switch,for继续
                case 2:
                    System.out.println("tuesday");
                    break;
                case 3:
                    System.out.println("wednesday");
                    break;
            }
        }
    }
}

2、continue

continue是只能用于循环结构。

表示提前结束本次循环,==本次==循环continue下面的循环体语句不执行,提前进入下一次循环的准备:

对于while和do…while来说,下一次循环的准备是指循环条件的判断;对于for来说,下一次循环的准备是指迭代表达式的执行和循环条件的判断;

如果有嵌套的情况,也是作用于最近的循环。

3.5.6 循环的嵌套

一句话:

外层循环循环一次,内层循环循环一轮。内层循环是作为外层循环的循环体语句。

第四章 数组 4.1 数组的概念

数组:是一个容器,也是一种数据的集合。容器是用来装东西的,数组是用来装一组数据,而且这组数据它们的类型是一样的。

数组这种容器的特点:

(1)元素是连续存储。

数组根据[下标]的方式访问元素的方式,效率是非常高,可以根据数组名中记录的首地址 和 下标直接 计算出元素的存储位置。

(2)数组的长度一旦确定,就不能更改,除非创建新的数组。

数组中每一个数据称为元素,数组中元素的总个数称为数组的长度,每一个元素是通过[下标]来进行区分的,下标的范围是[0, 数组的长度-1],如果下标指定超过这个范围了,会报一个==ArrayIndexOutOfBoundsException==数组下标越界的异常。

数组的好处:可以用一个数组名,统一管理一组数据。

数组的分类:

按照维度来分:一维数组和二维数组

按照元素的类型划分:元素是基本数据类型 和元素是引用数据类型

4.2 数组的声明和使用 1、数组的声明
元素的类型[] 数组名;

    public static void main(String[] args) {
        //Java中推荐的数组写法,也是阿里规范中推荐的写法
        //元素的类型[] 数组名
        //例如:
        int[] arr1;
        //为什么Java推荐这种写法?
        //因为在Java中 int[]是一种数据类型,称为数组类型
        System.out.println(int[].class);

        //但是Java为了照顾C语言的程序员习惯,也支持如下写法
        //元素的类型 数组名[];
        //例如:
        int arr2[];
    }

2、数组的初始化 初始化的目的:确定数组的长度和元素。 (1)静态初始化
//写法一:声明和初始化是一句完成的
元素的类型[] 数组名 = {元素的值列表};

//写法二:声明和初始化不是一句完成的
元素的类型[] 数组名;
数组名 = new 元素的类型[] {元素的值列表}; //此处注意右边的[]中不要写长度,因为此时的长度由{}中元素的个数决定

(2)动态初始化
元素的类型[] 数组名 = new 元素的类型[长度];

元素的类型[] 数组名;
数组名 = new 元素的类型[长度];

3、获取数组的元素和长度
数组的元素表示方式:
    数组名[下标]

数组的长度表示方式:
    数组名.length

4、数组的遍历
for(int i=0; i<数组名.length; i++){
    数组名[i]表示元素
}

4.3 数组的存储

数组的元素是存在==“堆”==中,当我们使用new关键字时,表示在堆中申请一块“连续”的存储空间用来存储数组的元素等信息。

数组名中记录的是这块连续存储空间的==首地址==。

4.4 数组的基础算法 4.4.1 对元素统计分析

1、累加和

所有元素都累加,所以累加的语句 sum += 元素的语句,不加条件控制的。

int[] arr = {.....};//假设arr.length=5

//定义一个变量用来存储总和
int sum = 0;

for(int i=0; i 

2、累加偶数和

不是所有元素都累加,所以累加的语句 sum += 元素的语句,必须加条件控制。

int[] arr = {.....};//假设arr.length=5

//定义一个变量用来存储总和
int sum = 0;

for(int i=0; i 

3、统计偶数个数

int[] arr = {.....};//假设arr.length=5

//定义一个变量用来存储个数
int count = 0;

for(int i=0; i 

4、统计素数个数

int[] arr = {.....};//假设arr.length=5

//定义一个变量用来存储个数
int count = 0;

for(int i=0; i1){//满足素数的条件再累加个数
        count++;
    }
}


for(int i=0; i1){//满足素数的条件再累加个数
        count++;
    }
}

4.4.2 找最大值/最小值

只是找最大最小值,是不需要考虑元素重复。

int[] arr = {....};

//对于元素已经已知的数组
int max = arr[0];//先假设第一个元素的值最大
int min = arr[0];//先假设第一个元素的值最小

for(int i=1; i max){
        max = arr[i];
    }
    if(arr[i] < min){
        min = arr[i];
    }
}


for(int i=0; i max){
        max = arr[i];
    }
    if(arr[i] < min){
        min = arr[i];
    }
}

4.4.3 找最大值/最小值下标

如果要找下标,那么就要分两种情况,一种是元素不会重复,一种是元素有重复。

(1)一种是元素不会重复

以最大值为例

int[] arr = {....};

//对于元素已经已知的数组

int max = arr[0];//先假设第一个元素的值最大
int index = 0;
for(int i=1; i max){
        max = arr[i];
        index = i;
    }
}


int maxIndex = 0;//先假设第一个元素的值最大
for(int i=1; i arr[maxIndex]){
        maxIndex = i;
    }
}

(2)另一种是元素有重复

以最大值为例:

必须分两步走,先找出最大值,然后再找下标

int[] arr = {....};

//对于元素已经已知的数组
//第一轮遍历,目的是找最大值
int max = arr[0];//先假设第一个元素的值最大
for(int i=1; i max){
        max = arr[i];
    }
}


//第二轮遍历,目的是找最大值的下标
System.out.println("最大值的下标有:");
for(int i=0; i 
4.4.4 查找元素下标 

1、顺序查找:如果数组无序,只能选择顺序查找。

2、二分查找:如果数组有序,优先选择二分查找更快。

数组有序的话,既可以二分查找,又可以顺序查找。

1、顺序查找
int[] arr = {....};//数组不要求有序
int target = ?; 

//每一个元素都比较一遍
for(int i=0; i 
2、二分查找 
int[] arr = {....}; //数组必须有序 ,假设是从小到大
int target = ?; 

//先不考虑重复
int index = -1;
//boolean flag = false;//假设没找到
for(int left=0, right=arr.length-1; left<=right;){
    int mid = left + (rigth-left)/2;
    
    if(target == arr[mid]){
        index = mid;//只要修改了index,index一定不会是-1,因为正常的下标是从0开始的正数
        //flag = true;//找到了
        break;
    }else if(target > arr[mid]){
        //说明目标应该在[mid]右边,移动左边界
        left = mid + 1;
    }else{
        //说明目标应该在[mid]左边,移动右边界
        right = mid - 1;
    }
}
                     
if(index==-1){
     System.out.println("不存在");                    
}else{
	 System.out.println("存在,下标是" + index);             
}

考虑重复元素(不要求都掌握,能掌握更好)

int[] arr = {....}; //数组必须有序 ,假设是从小到大
int target = ?; 

//先不考虑重复
int index = -1;
//boolean flag = false;//假设没找到
for(int left=0, right=arr.length-1; left<=right;){
    int mid = left + (rigth-left)/2;
    
    if(target == arr[mid]){
        index = mid;//只要修改了index,index一定不会是-1,因为正常的下标是从0开始的正数
        //flag = true;//找到了
        System.out.print("找到了,下标有" + mid);
        
        //找到了,说明arr[mid]是目标元素
        //那么要判断[mid]的左右位置是否也相同
        for(int j=mid-1; j>=0; j--){//看[mid]的左边
            if(arr[j] == target){
                 System.out.print("," + j);
            }else{
                break;
            }
           
        }
        for(int j=mid+1; j<=arr.length-1; j++){//看[mid]的右边
            if(arr[j] == target){
                 System.out.print("," + j);
            }else{
                break;
            }
        }
              
        
        break;
    }else if(target > arr[mid]){
        //说明目标应该在[mid]右边,移动左边界
        left = mid + 1;
        
        while(arr[left] == arr[mid]){
            left++;
        }
        //当arr[left] != arr[mid]就结束while,这里按照假设 left=9, arr[left]的值是8
        
    }else{
        //说明目标应该在[mid]左边,移动右边界
        right = mid - 1;
        
        
        while(arr[right] == arr[mid]){
            right--;
        }
        //当arr[right] != arr[mid]就结束while,这里按照假设 right=3, arr[right]的值是5
    }
}
                     
if(index==-1){
     System.out.println("不存在");                    
}else{
	 System.out.println("存在,下标是" + index);             
}
4.4.5 排序 1、直接选择排序

思路:每一趟找出本轮的最小值/最大值的位置,如果它不在应该在的位置,就与应该在的位置元素交换。

以从小到大为例,每一趟找出本轮的最小值为例

int[] arr = {....}; 


for(int i=0; i 
2、冒泡排序 

思路:每一轮都从头开始,相邻元素比较,如果相邻元素不满足最终的顺序,交换。

例如:要实现从小到大,那么如果前面的元素比后面的元素大,就交换。

int[] arr = {....}; 


for(int i=1; i arr[j+1]){
            int temp = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = temp;
        }
    }
}

如果考虑优化:

int[] arr = {....}; 


for(int i=1; i arr[j+1]){
            int temp = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = temp;
            flag = false;//只要交换,那么说明数组还不确定是完全有序的
        }
    }
    
    if(flag){
        break;
    }
}

第五章 面向对象(上) 5.1 概念

类:一类具有相同特性的事物的“抽象”描述。

对象:是某个类的具体的一个个体,实例。

抽象与具体的关系。

5.2 类的定义和对象的创建 1、类的定义(class)
【修饰符】 class 类名{
    //成员
}

注意:类名,尽量见名知意,每一个单词首字母大写

2、对象的创建(new)

(1)匿名对象

new 类名()

new 类名(实参列表)

(2)把对象赋值给一个变量,相当于给对象命名

类名 变量名/对象名 = new 类名(【实参列表】);

5.3 包 1、包的作用

(1)避免类的重名

(2)组织管理不同功能的类

(3)控制某些类等的可见性范围

只有public的类才能跨包使用。

2、包的声明
package 包名;		

注意:

(1)包名要小写,每一个单词用.分割

(2)包名不能以java开头,包名也不能直接使用关键字

(3)包名习惯用公司域名倒置 + 业务模块名

(4)package语句必须在.java文件的首行,一个.java文件只能有一个,而且对应的.java文件必须在对应包目录结构中。

3、如何跨包使用类
方式1:使用全名称:包.类名
方式2:在类上面写import 语句; 代码中使用简单的类名

import java.util.Scanner;

public class Test{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
    }
}

5.4 成员变量 1、成员变量声明的位置
类中方法外

2、成员变量声明的格式
【修饰符】 class 类名{
    【修饰符】 数据类型 变量名;
}

3、成员变量的特点

目前讨论的都是没有static修饰的。

(1)有默认值
(2)所有对象的成员变量的值都是“独立”的
4、成员变量的使用

目前讨论的都是没有static修饰的。

(1)在本类中使用

如果没有形参等局部变量和成员变量重名,直接使用。
如果有形参等局部变量和成员变量重名,加this.成员变量

(2)在别的类中使用

先创建对象
再通过对象.成员变量的方式使用
public class Rectangle {
    double length;
    double width;

    void setLengthAndWidth(double length, double width){
        //这里的this表示当前对象,这里是哪个对象呢?
        //哪个对象调用这个方法,this就是哪个对象
        //这里加this的作用是用于区别“成员变量”和“形参”
        this.length = length;
        this.width = width;
    }

    double area(){
        return length * width;
    }
}
public class RectangleTest {
    public static void main(String[] args){
        Rectangle r = new Rectangle();
        r.length = 5;
        r.width = 3;
    }
}
5、成员变量的存储

目前讨论的都是没有static修饰的。

成员变量是存在”堆“中,每new一次,创建一个对象,给这个对象开辟对应的内存空间,用来存储它的成员变量的值。

5.5 成员方法 1、方法的概念和好处
方法是一个“独立的" "可复用的" "功能"。

2、方法的定义格式
【修饰符】 class 类名{
    //成员变量
    【修饰符】 数据类型 变量名;
    
    //成员方法
    【修饰符】 返回值类型  方法名(【形参列表】){
        【方法体语句块;】
    }
}

(1)方法名

尽量见名知意,而且从第二个单词开始首字母大写。

(2)返回值类型

void:表示方法没有结果返回

方法体中可以有 return ; 语句,不能有 return 结果; 语句 非void:表示方法有结果返回

可以是int等的基本数据类型也可以是String、int[]、Circle等各种引用数据类型方法体中必须有 return 结果; 语句,而且要保证方法体执行的最后一条语句是 return语句。

(3)(【形参列表】):表示方法中有未知的变量,它的初始化需要调用者来传参数进行赋值

():表示方法没有形参,但是()也不能省略,而且这个方法调用时,不需要传“实参”(数据类型 形参名, 数据类型 形参名):表示方法有形参,()也不能省略,而且这个方法调用时,需要传“实参” 3、方法的调用格式

无论在哪里使用方法,()都不能省略。

(1)在本类中

直接使用

(2)在其他类中

先创建对象
再通过对象.成员方法的方式使用

package com.atguigu.method;

//Rectangle矩形
public class Rectangle {
    double length;
    double width;

    void setLengthAndWidth(double length, double width){
        this.length = length;
        this.width = width;
    }

    double area(){
        return length * width;
    }
    double perimeter(){
        return 2 * (length + width);
    }
    String getInfo(){
        return "长:" + length + ",宽:" + width +",面积:" + area() +",周长:" + perimeter();
    }
}


public class TestRectangle {
    public static void main(String[] args) {
        Rectangle r = new Rectangle();
        r.setLengthAndWidth(5,3);
        System.out.println(r.getInfo());
		System.out.println("单独看面积:" + r.area());
    }
}

4、形参和实参

形参是指方法==“声明”==时()中定义的变量。

【修饰符】 class 类名{
    //成员变量
    【修饰符】 数据类型 变量名;
    
    //成员方法
    【修饰符】 返回值类型  方法名(【形参列表】){
        【方法体语句块;】
    }
}

class Demo{
    //方法的定义,方法的声明
    void printInt(int a){//(int a)就是形参
        System.out.println(a);
    }
}

实参是指方法==“调用”==时()中传入的值(常量值、变量值、表达式的值)。

class Test{
    public static void main(String[] args){
        Demo d = new Demo();
        
        //这里是调用/使用printInt(int a)方法
        //调用就是让printInt(int a)方法执行
        d.printInt(5);//(5)就是实参,而且是常量值的实参
        
        int num = 6;
        d.printInt(num);//(num)就是实参,而且是变量值的实参
        
        d.printInt((int)(Math.random()*100));//((int)(Math.random()*100))就是实参,而且是表达式的值作为实参,这个表达式是 (int)(Math.random()*100),调用Math类的random()方法,产生随机值,这里是产生一个[0,100)的整数的表达式
    }
}

5、return关键字

两种使用的形式:

(1)return ;

(2)return 结果;

return ;
这种形式只能出现在方法的返回值类型是void的方法体中。
作用表示提前结束方法体的运行。表示结束方法但不返回结果。
那么调用这种方式的表达式只能直接加;单独成一个语句。

public class TestReview {
    public static void main(String[] args){
        Demo d = new Demo();

        //这里是调用/使用printInt(int a)方法
        //调用就是让printInt(int a)方法执行
        d.printInt(5);//d.printInt(5)这个方法调用表达式,只能直接加;构成语句。

//        int num = d.printInt(5);//错误
//        System.out.println(d.printInt(5));//错误
    }
}
class Demo{
    //方法的定义,方法的声明
    void printInt(int a){//void表示不返回结果
        System.out.println(a);
    }
}

return 结果;
这种形式只能出现在方法的返回值类型“不是”void的方法体中。
作用表示提前结束方法体的运行。并且返回结果。
那么调用这种方式的表达式可以
A:直接加;单独成一个语句。(返回值会丢失)
B:可以用变量接收返回值
C:可以直接输出返回值
D:可以把返回值作为另一个方法调用的实参
E:可以把返回值作为另一个表达式的操作数
...

package com.atguigu.review;

public class TestReview2 {
    public static void main(String[] args) {
        Example e = new Example();

        e.max(4,6);//直接加;构成语句,max方法执行了,并且有结果返回,但是没有接收,就丢了

        int bigger = e.max(7,9);//用变量接收返回值
        System.out.println("bigger = " + bigger);

        System.out.println("最大值是:" + e.max(8,2));//直接输出返回值

        int biggest = e.max(e.max(4,6),9);//e.max(4,6)的返回值作为 第二次e.max方法调用的实参
        System.out.println("biggest = " + biggest);
        
        int x = 1;
        int y = 3;
        int result = e.max(x,y) + 5;//用x,y中的最大值 + 5,这里把e.max(x,y)返回值作为表达式的操作数
        System.out.println("result = " + result);
    }
}

class Example {
    int max(int a, int b) {
        System.out.println("Example.max");
        return a > b ? a : b;
    }
}

6、方法的调用过程

入栈:一旦方法被调用执行就会在“栈”中开辟一块独立的内存空间,用于存储方法的“局部变量”等信息。

出栈:一旦方法运行结束,就会自动释放对应的内存空间。Java中只要这个方法体没有完全执行结束,那么就不会彻底释放对应的空间。

栈:先进后出。

7、局部变量和实例变量的区别

(1)声明的位置不同

实例变量:类中方法外局部变量:属于方法里面

【修饰符】 class 类名{
    //成员变量
    【修饰符】 数据类型 实例变量;//没有static修饰的成员变量
    
    //成员方法
    【修饰符】 返回值类型  方法名(【形参列表】){ //(【形参列表】)也是方法的局部变量
        【方法体语句块;】 //这里面声明的变量都是局部变量
    }
}

(2)在内存中存储值的位置不同

实例变量:堆局部变量:栈

(3)作用域

实例变量:整个类中(非静态static的方法中)局部变量:从声明处开始,到它所属结构的{}结束

(4)生命周期

实例变量:和所属的对象生命周期一样局部变量:随着方法调用而分配,方法调用结束就自动释放

(5)初始化方式不同

实例变量:有默认值局部变量:必须手动初始化

(6)修饰符

实例变量:有很多局部变量:只能有一个final 8、可变参数

如果形参声明时,在数据类型后面加...就是可变参数。

要求:

(1)一个方法只能有一个可变参数

(2)可变参数的声明必须是形参列表的最后一个

调用时:

(1)可变参数部分,可以传入对应类型的数组

(2)可变参数部分,可以传入对应类型的0~n个元素

在声明它的方法中,当数组使用即可。

class Demo{
    //String s和 double d不属于可变参数
    //int...nums才是可变参数
    void method(String s, double d, int... nums){
        //在这里要使用nums,就把nums当成数组使用即可
    }
}

class Test{
    public static void main(String[] args){
        Demo d = new Demo();
        
        d.method("hello", 5.2);//"hello"这个实参是给 String s这个形参赋值的
        						//5.2这个实参是给double d这个形参赋值的
        						//这里可变参数部分没有传入实参
        
        d.method("hello", 5.2, 1,2,3,4,5);//"hello"这个实参是给 String s这个形参赋值的
        								//5.2这个实参是给double d这个形参赋值的
        								//这里可变参数部分传入5个实参,1,2,3,4,5
        
        d.method("hello", 5.2, new int[]{1,2,3,4,5});//"hello"这个实参是给 String s这个形参赋值的
        								//5.2这个实参是给double d这个形参赋值的
        								//这里可变参数部分传入1个数组
    }
}

9、命令行参数(了解)
给main方法传的参数称为命令行参数
class Test{
    public static void main(String[] args){
        //args就是一个数组
    }
}

运行时,可以通过java命令传入实参
java Test 参数1 参数2 
10、方法的参数传递机制(重要,容易出面试题)

方法的形参是基本数据类型(8种),那么实参给形参的是“数据值”的“副本”,传完值之后,实参和形参就无关了。即无论形参怎么修改,和实参都无关。方法的形参是引用数据类型(可以是类、可以是数组等),那么实参给形参的是“地址值”的“副本”,相当于此时形参和实参“指向”同一个“对象”。即此时形参修改对象的属性(属性就是成员变量)的值,和实参自己修改是一样的。

但是这里要警惕一种情况,如果形参又指向了一个“新”的对象(形参名 = new 新对象),那么就和实参无关了,除非返回这个新对象,并让实参重新接收。 11、方法的重载

在同一个类中,方法名相同,形参列表不同(可以是个数不同,可以是类型不同)的几个方法称为方法的重载。和返回值类型是否一致无关。

当有多个方法重载时,调用时,会遵循如下的原则:

(1)先找最匹配的

实参的个数和类型与形参的个数和类型完全一致。

(2)再找唯一兼容的

实参的类型 < 形参的类型如果形参是可变参数,实参的个数比较灵活(0~n个元素,或者是数组都兼容)。

(3)如果找不到匹配的或兼容的,或者找到多个兼容的 都会报错。

12、方法的递归调用
当方法出现自己调用自己,就是递归调用。
必须有条件的递归调用,否则就会发生 StackOverflowError(栈内存溢出的错误)

5.6 对象数组

情况分为两种:

(1)元素类型是非数组以外的引用数据类型的一维数组。

Student[] students;  //元素是Student
Circle[] circles;    //元素是Circle
Rectangle[] rectangles; //元素是Rectangle

(2)元素类型是一维数组的一维数组。其实就是二维数组。

int[][] arr;  //元素类型看成int[]类型,当一维数组看成的
			  //元素类型是int类型,当二维数组看
char[][] letters; //元素类型看成char[]类型
			 //元素类型是char,当二维数组看

String[][] strings; //元素类型看成String[]类型
			//元素类型是String,当二维数组看
Student[][] students;//元素类型看成Student[]类型
			//元素类型是Student,当二维数组看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rh9KJ34u-1647162051493)(复习笔记.assets/image-20220312104048273.png)]

第6章 面向对象基础(中) 6.1 封装 1、封装的好处

隐藏内部细节,既可以安全,又可以方便/简化使用者的负担。使得可控。成员变量赋值的可控(通过方法可以使得非法值不会赋值进去)、成员的可见性范围可控。 2、封装的实现

封装就是靠权限修饰符来控制的。

本类本包其他包的子类其他包的非子类
private
缺省(不写)
protected
public

这些权限修饰符可以修饰的角色:

class(外部类)成员变量成员方法。。。
private
缺省(不写)
protected
public
3、成员变量私有化和get/set
package com.atguigu.review;

public class Employee {
    private String name;
    private int age;
    private char gender;
    private double salary;
    private boolean marry;
    
    //快捷键:Alt + Insert

    public String getName() {//调用get方法的目的是为了获取某个属性的值
        return name;
    }

    public void setName(String name) { //调用set方法的目的是为了修改某个属性的值,或者说给某个属性赋值
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public boolean isMarry() {//get换成了is
        return marry;
    }

    public void setMarry(boolean marry) {
        this.marry = marry;
    }
}

6.2 继承 1、继承的好处/目的

(1)代码的复用:在子类中复用父类的所有的成员变量和成员方法。(如果从逻辑意义上来说,就是要在原有的某个事物中再细分出更加具体的某个子类别)

(2)代码的扩展:在子类中扩展父类中没有的成员变量或成员方法。(如果从逻辑意义上来说,就是要表现的更加具体,更加具体就是通过更多的成员变量来进行描述,更多的可用功能来表示)。

(3)表示两个事物之间的is-a的关系。

2、问题?子类和父类谁大?

这里大家要明确这里,比较大小的关系是从事物的范围角度来说。

父类 > 子类

父类能表示的范围更广。子类更具体,范围更窄。

父类是Person,子类是Man,Person的范围更大,Man的范围更小。

错误的理解?子类的成员更多,应该是子类>父类才对。错误的原因是,成员更多只是表明它更具体而已,,不是更大。

3、继承的语法

【修饰符】 class 子类 extends 父类{
}
4、继承的要求和特点

(1)Java的类只支持单继承==>只有一个亲生父亲

(2)Java支持多层继承,即表示父类仍然可以有父类,父类的父类的成员也一并会继承到子类中。==>代代相传

(3)同一个父类可以同时存在多个子类。 ==>子孙满堂

(4)父类中所有的成员变量和成员方法都会继承到子类中。(如果从逻辑意义上来说,子类是父类的某个子类别,而类的定义是一类具体相同特性的事物的抽象描述,那么父类中应该是所有子类共同的特征。那么子类在继承父类时,就应该会继承父类所有的特点)。但是要注意的是,在父类中声明为private,在子类中不能直接使用。跨包的话,在父类中声明为private和缺省的,在子类不能直接使用。

但是这个特征子类要有,意味着在创建子类对象时,就要检查所有分类都声明了什么成员变量(哪怕是私有的),在子类对象中就要为这些成员变量开辟内存空间。

注意:父类对象中的值,不会继承到子类中。

(5)如果父类中某个方法的实现不适合子类,那么子类可以对这个方法进行重写。但是要注意,重写时要遵循重写的要求。

5、方法重写(Override)

要求:

(1)方法名必须相同

(2)形参列表也必须相同

(3)返回值类型

基本数据类型和void:必须相同

引用数据类型:<= (子类重写父类的方法时,子类重写的方法的返回值类型 可以是父类被重写方法的返回值类型的子类)

class Father{
    public A method(){
        //.....
    }
}
class Son extends Father{
    public B method(){
        //.....
    }
}
class Sub extends Father{
    public A method(){
        //.....
    }
}

//B是A的子类

(4)权限修饰符:>=

如果父类被重写方法的权限修饰符是private的,那么子类是无法进行重写。如果父类被重写方法的权限修饰符是缺省的,那么本包的子类可以重写,跨包的子类就不能重写。如果父类被重写方法的权限修饰符是protected,那么无论是本包还是跨包的子类,都可以对它进行重写,而且重写后的方法的权限修饰符可以是protected,也可以是public。如果父类被重写方法的权限修饰符是public,那么无论是本包还是跨包的子类,都可以对它进行重写,而且重写后的方法的权限修饰符只能是public。

(5)其他要求(待补充)

注意:

方法重写(Override)和方法重载(Overload)容易混淆?

方法重载的要求:方法名相同,形参列表必须不同,不看返回值类型,也不看权限修饰符。严格来说,方法重载是在一个类中的两个或多个方法。如果宽泛一点,如果子类定义了和父类某个方法名相同,形参列表不同的方法,也可以归到重载中。

6.3 多态 1、多态的好处

(1)可以让代码编写更灵活

即某个变量是A类型,那么给这个变量赋值可以是A类的对象,也可以是A类的子类对象。

(2)让Java也支持动态绑定技术

即某个变量编译时是A类型,那么通过这个变量调用某个方法时,它不一定是执行A类中的方法体,可能是调用子类“重写的”方法体。

class A{
    public void method(){
        //....(1)
    }
}
class B extends A{
    public void method(){
        //....(2)
    }
}

class Demo{
    private A a;
    
    public void test(){
        a.method();//看起来它应该执行 (1),但是实际上不一定执行的是(1),可能是(2)
        			//什么情况下执行(2),当a变量被赋值为B类的对象。
        //多种形态,a对象可以是很多情况。可以是A类自己的对象,也可以是A类的子类的对象
    }
}

2、多态的现象发生的前提条件

(1)继承:父子类关系

(2)虚方法的重写

什么是虚方法?可以被重写的方法。

3、多态的现象

编译时看父类,运行时看子类

某个变量它的类型有两种情况:

A a = new B();
a.虚方法()
//从编译的角度来说,a变量是A类型,称为编译时类型
//从运行的角度来说,a变量是B类型,称为运行时类型
4、多态的应用场景

(1)某个变量(局部变量、成员变量、形参)声明为父类类型,而实际赋值的是子类对象

(2)某个方法的返回值类型是父类类型,而实际返回的是子类的对象

(3)某个数组声明时,元素类型声明为父类类型,而实际存储的元素对象是子类对象

要特别注意,此时这些情况拿到的变量或元素在调用虚方法时,警惕它可能执行的是子类重写的方法。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/763820.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号