#常见计算机命令
cd--打开目录; .. 表示上一层目录;.表示当前路径 /表示根目录
tree--显示指定目录下的树状结构
dir--显示指定目录下的所有子文件和子目录
mkdir--创建目录
rmdir--删除目录,非空目录不能删除
del--删除文件
#java
1995年SUN公司 跨平台语言
JVM--JAVA虚拟机 是Java跨平台的前提 它本身不能跨平台
Java的技术结构:
J2SE--标准版
J2EE--企业版
J2ME--移动版
JRE-- JAVA RUNTIME ENVIRONMENT java运行时环境 JVM+核心类库
JDK-- Java Development Kit java开发工具包 JRE+开发工具
#编译
将代码翻译成机器语言。
Java没有主函数也可以编译;
每一个Java类编译后都生成一个class文件;
Javac -d . Demo.java : -d 表示带包编译 . 代表当前目录
#环境变量
为操作系统指定一些运行时所需要的量;
JAVA_HOME Java的安装路径
Path %JAVAHOME%\bin;
#关键字
在Java中被赋予了特殊含义的关键字。
一共有53个,其中51个在用,还有两个没有使用---const,goto,叫保留字。
关键字全部小写。
关键字表格
用于定义数据类型的关键字 | |||||||
class | interface | byte | short | int | long | ||
float | double | char | boolean | void | enum | ||
用于定义数据类型值的关键字 | |||||||
true | false | null |
|
|
| ||
用于定义流程控制的关键字 | |||||||
if | else | switch | case | default | while | ||
do | for | break | continue | return |
| ||
用于定义访问权限修饰符的关键字 | |||||||
private | protected | public |
|
|
| ||
用于定义类、函数、变量修饰符的关键字 | |||||||
abstract | final | static | synchronized |
|
| ||
用于定义类与类之间关系的关键字 | |||||||
extends | implements |
|
|
|
| ||
用于定义建立实例、判断实例的关键字 | |||||||
new | this | super | instanceof |
|
| ||
用于异常处理的关键字 | |||||||
try | catch | finally | throw | throws |
| ||
用于包的关键字 | |||||||
package | import |
|
|
|
| ||
其他修饰符关键字 | |||||||
native | strictfp | transient | volatile | assert |
|
#标识符
命名规则:
1.可以由字母、数字、下滑线_、$,组成,但不能是纯数字,且数字不能作为开头
2.不能使用关键字作为标识符。
3.尽量见名知意。
4.Java支持中文命名,但是不推荐。
5.尽量少用 $ 。
驼峰命名法:
类名/接口名:由多个单词组成时,每个单词首字母的大写。XxxYyy
变量名/方法名:由多个单词组成时,第一个单词首字母小写,其余单词首字母大写。
包名:由多个单词组成时,所有字母小写,用 . 隔开。xxx.yyy
常量名:由多个单词组成时,所有单词大写,中间用_隔开。NUM_STU_SIZ
#注释
在程序中用来解释说明程序的文字。作用:解释程序,排除错误
//注释文字(用于注释单行)
/* 注释文字 */(多行注释)
/** 注释文字 */(文档注释)--注释的文字可以用工具提取出来形成文档。
命令:javadoc Demo.java 只能提取公共类
javadoc -d . Demo.java 若指定路径应事先存在
#进制
在JDK1.7中,允许以0b作为二进制开头
二进制:以 0b 开头,满2进1,0~1;
八进制:以 0 开头,满8进1,0~7;
十进制:满10进1,0~9;
十六进制:以 0x 作为开头,满16进1,0~9,A~F
进制的相互转换:
1.取余倒排
2.先转换成二进制,二进制再转 需要的进制
二进制→八进制:从低位开始,3位一组,位数不足补0 (001,010,011……)
八进制→二进制:一位变三位的过程
每一位上的八进制数字按照十进制转二进制
二进制十六进制与二八进制类似。
#常量
整数常量——所有的整数,1,2,3....
小数常量——所有的小数,1.1,6.3,623.1
字符常量——将一个字母,数字或者符号用单引号标示起来,'a' ,'+' ,不允许''
字符串常量——将一个或者多个字符用双引号表示起来,"sss" ,"dd" ,"a" ,表示空的""
布尔常量——true / false 用以表示逻辑值
空常量——null 表示值为空
例: 2 --整数~ 2.0 --小数~ '2' --字符~ "2" --字符串~
#变量
记录存储数据;
变量应先定义,后使用;
初始化--在程序中第一次为变量赋值;
变量应 该先赋值后使用
变量的使用范围--在哪里定义就在他的{}中使用
#转义字符
回车:\r 双引号:\"
换行:\n 反斜杠:\\
制表符:\t 单引号:\'
#数据类型
基本数据类型,引用数据类型
扩充:位bit 8 bit = 1 byte
1 byte = 1024kB .....MB GB TB PB
基本数据类型
数值型:
1.整数型:
byte--字节型--1个字节; 范围:-2^7 ~ 2^7 - 1—— -128~127;
short--短整型--2个字节;范围:-2^15 ~ 2^15 - 1—— -32768 ~ 32767;
int--整型--4个字节; 范围:-2^31 ~ 2^31 - 1;
例如:int j = 7_343_434_789(在JDK1.7中才允许的);
int i = 000001111(八进制数据)
在Java中整数就是默认int类型
long--长整型--8个字节; 范围:-2^63 ~ 2^63 - 1;要求以L结尾,标示这是一个long类型数据;
2.浮点型:
float--单精度--4个字节; 范围:
要求以 f 结尾
double--双精度--8个字节;范围:
在Java中小数默认double类型;在Java中允许以D结尾表示这个小数是一个double类型数据, 但是不推荐;
double d = 3e5; //十进制的科学计数法,3 x 10^5
double d = 0x3p2; // p是十六进制的科学计数法 p2是2的平方;
注意:double类型数据不能精确的保存小数;
以上数据类型都是有符号的,可以存储负数。
初始值:byte/short/int---0,long---0L,float---0.0f
double---0.0,
char--- ‘\u0000’,
boolean--false
其他的引用数据类型的初始值是null
字符型:
1.char--2个字节--0 ~ 65535 默认值是 ’/u0000’
码表---ASCII码表; 西欧码表(ISO-8859-1),每个字符用一个字节表示;
国内 GB2312--两个字节表示一个字符;现在发展为GBK;
Unicode编码表 --常用的是UTF-8--- 他用3个字节表示一个字符; Unicode编码体系中的UTF-8编码,收录常见语言的常见字符,3个字节表 示一个字符。称作万国码表。
GB2312----简体汉字----2个字节表示一个汉字 ,后来发展为GBK
所有的码表默认兼容西欧码表
布尔类型:
布尔型----boolean----大小根据操作系统和jdk版本决定----true、false
引用数据类型
类--class
接口--interface
数组--[]
#数据类型的转换
隐式转换
- 小的数据类型可以转换为大的数据类型
Byte b = 3;
Int i = b; 可以
byte-->short-->int-->long
long l = 34; //34默认是int类型,int类型的值可以赋值给long类型,但 要在int类型范围内,如果超出范围,必须要加L结尾。
- Float-->double
- Int-->float 可能会产生精度损失 int i= 3;float f = i;
- 任何一个整型都可以赋值给浮点型的变量,但可能会产生精度损失
Float f = -25;//true
Float f= 25.0;./false
Long l = 34;
Float f = l; ------true
Int -->double也会产生精度损失 double用11位存储整数。
- Char-->int
这两种写法在编译时期就已经转换成了对应形式
Char c = 98; --- true ------ char c = ‘b’;
Short s = ‘b’; ---true ------- short s = 98;
//分开编译char c = ‘b’,已经在内存中
//于分开赋值所以两句话是分开编译的,并没有实际检查字符串的取值,无法确定范围。short和char取值范围没有完全重合,这就可能导致超出的问题,所以不允许这样赋值。反过来也不行
Char c = ‘b’;
Short s = c; --- false
注意: 各类基本数据之间是平等的,不存在大小或者父子关系。
#显示类型转换/强制转换
- 当把大的类型转换成小的类型的时候,需要强制转换
Int i = 3;
Byte b = (byte)i;
- 当把浮点型数据转换为整形变量的时候,需要强制类型的转换。小数转换成整数时,不是四舍五入,而是舍弃掉小数部分,因此,小数转换成整数时,也会产生精度损失。
注意:在计算机中,绝大部分小数是没有办法精确显示的
Float f = 3; -->3.0f
Int i = (int)f;
Double d =3.8;
Int i = (int)d -->i = 3;
#运算符
算术运算符,赋值运算符,逻辑运算符,比较/关系运算符,位运算,三目/三元/条件运算符
加+、减-、乘*、除/、取模%、自增++、自减--、字符串拼接+
- 两个int类型运算完成后,结果一定是int类型
Int i = 4350 / 1000 * 1000; i = 4
- Byte和short类型进行运算时底层会自动提升为int类型,然后计算
Byte b = 3 + 4; --->true
在编译时jvm会进行优化,优化完成后变成了byte b = 7
Byte b =2;
Byte b2 = b +3;------.false 3默认为int类型
Byte b= 2;
Byte b2 = 3
Byte b3 = b+b2 ---->false b+b2运行结果为int
3.当整数除以0的时候,编译无错,运行出错--ArithmeticException算数异常
4.当小数除以0的时候,编译无错,结果是Infinty--无穷大
5.当0除以0的时候,结果是NaN--Not a Number --非数字
- 尽量较少使用小数作为循环控制条件,因为小数不能精确显示
- Strictfp --- 在函数执行过程中,小数是以80位二进制,但是函数完成后,依然使用double类型存储最终的结果,最终的结果依然是64位二进制。
取模运算% ----取余
1.当一个小的数取余一个大数的时候,结果是那个较小的数:
5%3=2; 2%3= 2;
2.对于负数的取余,先按照正数的取余,然后看取余符号左边的符号,符号 左边是正数,结果符号就是正数;
-5%3=-2 -1%3=-1 -5 % -3 = -2 1 % -3 = 1;
3.Java中小数可以取余
3.2%2=1.2; 3%1.2=0.6 3.5%1.2=1.1;
自增运算++
相当于在原来的基础上加1,只有自增运算的时候,++在前在后一样。
++在前,变量先自增,然后再参与其他运算;
Int i = 5; int j = ++i; i=6,j=6;
++在后,变量先参加其他运算,再自增。
Int i = 5; int j = i++; i=6,j=5;
Byte类型能够进行自增运算吗?
Javap -c -v -s
注意:byte/short类型的变量可以使用 ++ 运算,在运算过程中依然提升为int类型,但在最后赋值时,JVM会将结果强制转换为byte或short类型
+=运算自带强制转换功能,所以byte和short可以使用 +=。
字符串的拼接+
任何数据去拼接字符串结果都是字符串;
“a” + ”b” --->”ab” 3 + true + "d" = 报错!
2 + “a”--->”2a”
2 + 3 + ”a” --->”5a”
2 + ‘a’ --->99
“a” + 2 + 3 --->”a23”
“a” + true--->”atrue”
赋值运算符
= , 加等于+= , 减等于-= , 乘等于*= , 除等于/= , %= ,&= , |= , ^=, >>= , <<=
Int i = 5; 赋值运算符;
i += 2; ---> i = i + 2;
F += 5 --->f = f+5
注意:Java不支持连等 int i= j= k= 5 --->false
Int i = 5;
i += i -= i *= i++; i= 5+(5-(5*(5)))=
i = += i -= i *= i++ * 2; i= 5+(5-(5*(10))) = -40
比较/关系运算符
==、 !=、 >、 <、 >=、 <=、 instanceof
Instanceof用来判断对象与类的关系。
逻辑运算符
&与(and)、 |或(or)、 !非(not)、 ^异或 、&&短路与、 ||短路或
true & true = true; true & false = false;
false & true = false; false & false =false;
true |true = true; true | false = true;
false | true = true; false | false = false
!true = false; !false = true;
true^true = false; true ^ false = true;
false ^ true = true; false ^ false = false;
&&短路与运算规则和&完全一样,但如果&&之前的结果确定为false,那么后边的表达式不再执行。
||短路或运算规则和|完全一样,但如果||之前的结果确定为true,那么后边的表达式不再执行。
位运算符位运算(只能操作整数)
&与运算,|运算,^异或运算, <<左移 , >>右移 ,>>>无符号
&与运算
例:3&4
3 二进制→ 011 转化为二进制,低位次对其,高位不足补0
0为false,1为true;
& 4 二进制→ 100 然后按位进行与运算,然后将数据转化为十进制 显示
——————————————————
000
或 | 运算 :
例:3|4
3 二进制→ 011 转化为二进制,低位次对其,高位不足补0
0为false,1为true;
| 4 二进制→ 100 然后按位进行或运算,然后将数据转化为十进制 显示
——————————————————
7 111
交换值的方式
方式一:
//借助第三方变量
int i = 3,j = 4;
int temp = i;
i = j;
j = temp;
方式二:
int i = 3, j = 4;
i = i + j;
j = i - j; -> j = i + j - j-> j = i;
i = i - j;-> i = i + j - i-> i = j;
i += j; j = i - j;i-=j;
方式三:
int i = 3, j = 4;
i = i ^ j;
j = i ^ j; -> j = i ^ j ^ j-> j = i
i = i ^ j; -> i = i ^ j ^ i-> i =j;
i^=j; j^=i; i^=j;
异或 ^ :
3 二进制→ 011 转化为二进制,低位次对其,高位不足补0 0为false, 1 为true;
^ 4 二进制→ 100 然后按位进行异或 ^运算,然后将数据转化为十进制显示
——————————————————
左移<< 右移>> 无符号右移>>>
三元/三目/条件运算符
逻辑/关系表达式 ? 表达式1 : 表达式a;
执行顺序:如果逻辑/关系表达式结果为true,那么执行表达式1,否则执行表达式2;
注意:三元表达式也是一个运算符,所以运算完成后一定有结果,可以 定义变量来接住这个结 果,此处已经输出没有变量接住结果。
三元表达式的结果的类型要么一致,要么能够互相转化。
嵌套:
int max = i > j ? i > k ? i > m ? i : m : k > m ? K : m : j > k ? j : k ;
int max = i > j ? i > k ? i : k : j > k ? j : k ;
int max = i > j ?
i > k ?
i : k
:
j > k ?
j : k ;
i > j ? System.out.println(i) : System.out.println(j) --- 编译错误!
System.out.println(i > j ? true:false); --- 语法,没有错误,但是多余,i > j有两个运行结果,true 或者false 没有必要使用三元表达式。
#流程控制
顺序结构:程序从上到下,从左到右依次运行的
分支结构:判断结构, 选择结构
判断结构
if(逻辑值){//逻辑值为false时不执行代码段
代码块;
}
if(逻辑值){
代码段;
}else{
代码段;
}
if(逻辑值1){
代码段;
}else if(逻辑值2){
代码段;
}else{
代码段;
}
选择结构
switch(表达式){
case 情况1:代码段1;break;
case 情况2:代码段3;break;
.......
default :代码段;break;
}
break关键字表示当前选项的结束,在switch语句中只有遇到了break 语句或switch语句末尾才结束
如果case语句后没有break,程序将会从匹配项之后的语句执行到最后
如果每个选项之后都有break,选项顺序不影响结果
表达式的结果:byte/short/int/char
JDK1.5开始,允许是枚举常量
jdk1.7开始,允许是String类型
//坑点:
int i = 1;
Switch(i){
Case 1: int j = 0;
Case 2 : j += 1;
Case 3 : j += 1;
Case 4 : j += 1;
Default: j += 4;
}
System.out.println(j); ------- 编译错误,j的定义范围在switch{}中!
判断的是一个范围时,建议使用if-else-if结构,如果情况相对固定,推荐使用switch-case语句。
循环结构
//while循环:
while(逻辑值){
代码块;
}
//在while语句中,如果代码块中只有一句,可以不加花括号,但尽量不省略。
//循环三要素:循环变量,控制循环条件,改变循环变量
//执行顺序:先判断逻辑值,为真执行代码块,然后继续判断是否满足 条件;为假则不执行代码块。
//do-while循环
do {
代码段;
}while(逻辑值);
//do ...while 代码至少执行一次 实际开发中使用的比较少
//for循环
for(定义循环变量;控制循环条件;改变循环变量){
代码段;
}
//当循环变量的改变相对比较规律的时候,建议使用for循环;
//当不确定循环次数时,建议使用while。
//While循环的循环变量定义在循环外,循环结束后循环变量依旧可以使用;for循环的循环变量是定义在for循环里边的,循环结束后,循环变量不能继续使用。
#break 和 continue
break:用于选择和循环语句中,表示终止当前的一层选择或者循环语句。
continue:用于循环结构(只用于循环结构),表示跳出当前循环,继续下次循环。
这两个关键字必须结合相应的语句使用,单独存在没有意义,因此单独存在时候编译报错。
循环的标号:
for(int i = 1; i < 4; i++){
for(int j = 1;j < 4; j++){
if(j == 2){
break;
}
}
}跳出第二层循环,i= 2 继续执行
for(int i = 1; i < 4; i++){
for(int j = 1;j < 4; j++){
if(j == 2){
continue;
}
}
}跳出本次循环 跳过j=2 从j = 3继续
Outer:for(int i = 1; i < 4; i++){
for(int j = 1;j < 4; j++){
if(j == 2){
continue Outer;
}
}
}满足条件后,直接跳出
当需要一次性跳出多层循环时,可以使用标号形式,但是不推荐,因为它破坏程序的阅读性。
#数组
数组:就是一组数的容器。
数组对每一个存入的数字都会编号,编号从0开始 --- 数组的下标从0开始。
数组的定义格式
1、数据类型[] 数组名 = new 数据类型[数组的大小或者元素个数];
例:Int arr = new int[5]; --- 表示一个存储5个整数的数组。
数组一但定义好,不可改变。
int i; ---- 变量的声明过程。
i = 5; --- 变量的初始化过程。
数组也可以先声明,再初始化。
例:int arr[]; arr[] = new int[5];
int arr[] = new int[5]; System.out.println(arr);
[ I @ 659e0bfd
[:表示对象的类型; “ [ ”表示这个对象是一个数组
I:如果这个对象是一个数组或是集合,那么这一位表示数组或集合中元素的类型 --- I 表示整型
@:表示后边的是内存地址
659e0bfd: 表示对象在内存中的地址对应的哈希码的32位二进制的十六进制表示形式。
2、数据类型[] 数组名 = new 数据类型[] { 元素1,元素2,...,...,..,..,.};
格式一:int[] arr = new int[5,5,4,1,2];
格式二:int [] arr; arr = new int[] {5,5,4,1,2}; --- 允许先声明后初始化
3、数据类型[] 数组名 = {元素1,元素2,........};
int arr = {1,2,4,5,8}; ---- 允许这样定义,在创建时动态获取数组的大小
int[] arr; arr = {1,2,3,4,5}; ----编译报错---因为编译器不知道开辟内存空间的大小
数组常见异常
ArrayIndexOutOfBoundsException ----- 数组下标越界,编译无错,运行报错;因为逻辑上或者语法上没有问题
NullPointException ----- 空指针异常;编译无错,运行出错,因为不能再null上进行任何操作。
Null只能是赋值给引用数据类型
#内存
Java将内存分为了5块:栈内存、堆内存、方法区、本地方法栈、寄存器
栈内存:存储变量,执行代码块,。变量存储在栈内存中不会自动赋予初始值,变量使用完成之后立即移出栈内存,释放空间。
堆内存:存储对象,对象在堆内存中会自动赋予初始值,对象在使用完成之后不会立即移除,而是在不定的某个时刻被回收。
数组在内存中的体现。
数组的扩容内存图:
注意:对于基本类型而言,传值传的是实际的数据;对于引用数据而言,传值传的是地址。
#数组的应用
1、获取数组中的元素:数组名[下标] int i = arr[2];
2、获取数组的长度:数组名.length; int i = arr.length;
3、遍历数组
//for循环
for(int i = 0; i < arr.length - 1; i++){
System.out.print(arr[i] + “ \t”);
}
//增强for循环,其中 i 代表的是arr数组中的元素
for(int i : arr ){
System.out.print( i + “ \t”);
}
//注意:增强for循环不能改变原来的数组
4、获取数组的最大值
int arr[] = {1,2,5,4,8,6};
int max = arr[0];
for (int i = 1; i <= arr.length - 1; i++) {
if( max < arr[i]){
max = arr[i];
}
}
System.out.println(max);
5、排序
冒泡排序
//冒泡排序
int[] arr = {2,8,5,6,1,7,4,3,7};
for (int i = 1; i < arr.length; i++) {
for (int j = 1; j < arr.length; j++) {
if(arr[j - 1] > arr[j]){
int temp = arr[j- 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
选择排序
//选择排序
int[] arr = {2,8,5,6,1,7,4,3};
for (int i = 1; i < arr.length; i++) {
for (int j = i ; j < arr.length; j++) {
if(arr[i - 1] > arr[j]){
int temp = arr[i - 1];
arr[i - 1] = arr[j];
arr[j] = temp;
}
}
}
拓展:快速排序、希尔排序、堆排序
Array.sort(数组) --- 只能从小到大
String s = Arrays.toString(arr) --- 将数组中的元素依次拿出来拼接成字符串
6、查找数组中的元素
查找无序数 --- for循环
int index = -1;
int num = 6;
for (int i = 0; i < arr.length; i++) {
if(arr[i] == num){
index = i;
break;
}
有序数组的查找 ---- 折半查找:
int[] arr = {2,8,5,6,1,7,4,3,9};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
int num = 100;
int max = arr.length;
int min = 0;
int mid = (min + max) / 2;
boolean b = true;
while(arr[mid] != num){
if(num >= arr[mid]){
min = mid;
}else{
max = mid;
}
if(min >= max){
b = false;
break;
}
mid = (max + min) / 2;
}
if(b){
System.out.println(mid);
}
7、数组的反转
//1.利用新数组
int[] arr = {2,8,5,6,1,7,4,3};
Arrays.sort(arr);
int arr1[] = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
arr1[arr.length - i - 1] = arr[i];
}
//2.利用两个变量同时操作数组两端
for(int start = 0, end = arr.length - 1; start < end ;start++, end--){
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
System.out.println(Arrays.toString(arr));
8、数组的扩容
数组的扩容,实际是数组的复制。
System.arraycopy(要复制的数组,要复制的数组的起始位置,被复制到的数组,新数组中放置的起始位置,要复制的元素的个数);
例:System.arraycopy(arr,3,arr2,2,5);---从arr下标为3的位置开始复制,复制5个元素到arr2中,从arr2的下标为2的位置开始依次存放
数组 = Arrays.copyOf(数组,长度);---表面上看起来对数组的长度做了改变,实际上数组已经发生了变化,已经是一个新的数组
T[] arr2; //数据类型在Java底层动态获取的
arr2 = new T[长度]; //T---表示泛型
if(长度 >= 数组.length){
System.arraycopy(arr,0,arr2,0,arr.length);
} else {
Sysetem.arraycopy(arr,0,arr2,0,长度);
}
arr = arr2;
#二维数组
二维数组是一组数组的容器 ----- 数组的数组
定义格式
1、数据类型[][] 数组名 = new 数据类型[二维数组大小、长度][一维数组大小]
int [][] arr = new int[3][5]; -- 表示一个包含3个一维数组的二维数组,每一个一维数组由5个一维数组组成
int[] arr2 = arr[0];----获取的是一个一维数组
arr[0][3];---如果获取具体的元素,需要两个下标
2、数据类型[][] 数组名 = new 数据类型[二维数组大小、长度][]
int[][] arr = new int[3][];---表示这个二维数组由3个整型的一维数组组成
int[][] arr = new int[3][];
arr[0][0] = 3 --- 不可以----因为所包含的一维数组没有初始化
3、数据类型[][] 数组名 = {{1,2,4,5},{4,1,0,3},{2,5,4}};
int[][] arr ={{1}{1,2}{1,2,3}};
arr[0][1] = 5; ---- 数组下标越界
经典面试题:
对于数组int[] x,y[]均已初始化,下列选项正确的是:BC
A.y = x;
B.y[0] = x;
C.y[0][0] = x[0]
D.y[0] = x[0]
E.y = x[0];
注意:[]在变量名之前是紧跟数据类型的,那么后边跟的每个变量至少是一个一维数组;[]在变量名之后是仅属于变量的。
#二维数组的应用
1、二维数组的长度 --- 数组名.length; 二维数组里边的每个一维数组长度 --- 数组名[下标].length
2、二维数组的遍历 ---- 双重for循环
for(int i = 0; i < arr.length; i++){ 遍历出来的每一个元素是一个一维数组
for(int j = 0; j < arr[i].length; j++){ //遍历对应位置上的一维数组
System.out.print(arr[i][j] +”\t”);
}
System.out.println();
}
3、二维数组的反转 --- 头尾交换
for(int start = 0, end = arr.length -1; start < end; start++,end--){
int[] temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
二维数组的内存图
#方法(函数)
将一段逻辑或者功能提取出来,这种提取形式就是函数。提高了代码的利用率。
格式
修饰符 返回值类型 函数名(参数列表){
方法体;
Return 返回值;
}
public static int add(int a,int b){ int sum = a + b; return sum; }
//明确返回值类型
//明确函数执行过程中是否有未知量参与,未知量不能自动产生,需要以参数的形式体现
//定义参数相当于声明了几个变量
//函数中声明的参数---形参---形式参数
//函数名+参数列表 叫做方法签名 ---add(int a,int b)
//调用函数时传入的值是实参,实际参数
注意: 1、如果没有返回值,那么返回值类型为void
2、return后的代码不再执行;
3、任何一个实体函数(有花括号的函数)都有return语句
4、当函数有具体的返回值类型时,函数必须有对应的返回值
Publlic static int m(int a){
if(a > 6){
return 10;
}//编译报错 因为if语句不一定会执行到
函数的重载
一个类中存在了函数名一致,参数列表不同的函数,就构成了函数的重载。
函数在调用时会最先匹配格式最一致的。
函数重载时尽量重载所有的情况,以防出现多个函数都匹配的情况:
函数的递归调用:指在函数中调用自己本身
StackOverflowError---栈溢出错误---反应函数在栈中执行---函数在执行完成之前不会释放栈内存---递归的次数太多,就会出现这个错误
#面向对象
面向对象是相对过程而言的,面向过程注重的是过程,强调的是动作;而面向对象注重的是对象,只要找到了对象,那么就自然拥有了对象所具有的一切功能。
面向对象是基于面向过程的。
面向对象一定优于面向过程吗?
当场景比较复杂时,建议使用面向对象;当事物比较简单的时候,建议使用面向过程。
类和对象的关系
根据一类对象的共有特征和共有行为进行抽取,将特征抽取成了属性,将行为抽取成了方法。
将这些属性和方法放到一个类中,用这个类表示这一类的事物 ---- 类是对象的一个抽取
根据类利用new关键字去创建该类对象的对象(实例)
对象在内存中的存储
成员变量和局部变量之间有什么不同?
1、定义位置:成员变量是定义在类方法外;局部变量是定义在方法或者
语句内部
2、作用域:成员变量作用在整个类中,局部变量只作用在定义的方法或者语句中
3、存储位置:成员变量是随着对象的创建存储在了堆内存中;局部变量在方法或者语句执行的时候存储到了栈内存中
4、生命周期不同:成员变量是随着对象的创建而加载,随着对象的销毁而销毁;局部变量在方法或者语句执行完成后立即移出栈内存。
构造函数
1、在创建对象的时候实际上是在调用构造函数
2、构造函数必须与类名一致,而且没有返回值
3、如果一个类中没有手动给定构造函数,那么程序在编译的时候JVM会 自动添加一个无参的构造函数
4、构造函数可以重载。
5、构造函数中可以有return 语句 作用:用于规避一些符合语法但不符合逻辑的情况出现。
6、可以出现与类同名的方法(函数),但不推荐。
#this关键字
注意:在Java中,所有的非静态属性和非静态方法都是通过对象来调用的
This代表当前对象的引用,由于类内没有实际对象的存在,所以通过this作为一个虚拟对象来调用本类中的其他方法和属性
可以通过this语句来调用本类中的其他对应形式的构造方法,this语句必须放在构造函数的首行
This语句:自动匹配本类中格式最对应的构造函数;
且在使用时必须放在构造函数的首行;
#构造代码块/初始代码块
在类内 方法外用{}定义的代码块---构造代码块/初始化代码块
在创建对象的时候先于构造函数执行
无论调用哪个构造函数都可以去执行某些必要的代码
#局部代码块
为了限制变量的生命周期,提高内存的利用率
坑点:int i = 0; int i = 0;
while(i < 10); while(i < 10)
;
{ {
System.out.print(i); System.out.print(i);
i++; i++;
} }
匿名对象
在代码中声明没有名字的对象,只能在创建的时候使用一次
#面向对象 —— 面向对象的特征
特征:封装 、继承 、多态 、 (抽象)
封装
封装的体现形式:函数、属性的私有化
为了防止在类外直接操作属性的时候给属性赋予一些不符合常识的值,因此将属性私有化,提供对外的访问方法来间接的操作属性
属性的私有化保护了数据的安全性
封装的优势:提高了代码的复用性,安全性。
Java中的权限修饰符(4个)
| 本类 | 子类 | 同包类 | 其他类 |
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
默认 | 可以 | 同包类可以 | 可以 | 不可以 |
private | 可以 | 不可以 | 不可以 | 不可以 |
继承
如果一些类中有一些共有的方法和属性,将这些方法和属性提取出来放到一个新的类中,然后利用extends关键字让原来的类和新类产生联系。新的类叫做父类(超类或基类),原来的类叫做子类(派生类)。
在Java中只支持单继承。多继承会导致方法调用的混乱。但Java支持多层继承。
优点:提高了代码的复用性;单继承使方法的调用更加安全。
在继承的时候,什么东西不能继承到子类中?
私有化的、代码块、构造函数(不存在继承,不存在重写)
单继承和多继承哪个好?
方法的重写
是指在父子类中存在了方法签名完全一致的方法,就产生了方法的重写
两等两小一大 5个要求
- 方法签名要求一致
- 如果父类中方法的返回值类型是基本类型/void/最终类,子类方法的返回值类型必须一致
- 如果父类的方法的返回值类型是引用类型,子类的方法的返回值类型是父类或者父类方法返回值类型的子类
class A {}
class B extends A{}
class C{
public A m(){
return null;
}
}
class D extends C {
public B m(){
return null;
}
}
- 子类方法的权限修饰符的范围要大于等于父类方法的权限修饰符范围
class A{
protected void m(){}
}
class B extends A{
public void m(){}
}
class A {
private void m() {
}
}
class B extends A {
public void m(){} ---不叫重写
public int m() { ---- 不叫重写
return 1;
}
}
#super关键字
在子类中代表了父类对象的引用,用于调用父类中的方法或者属性。
Super语句 --- 会去自动调用父类中对应形式的构造函数。
在子类的构造函数中,都会有一个super语句;如果父类中没有提无参构造,那么子类的构造函数中必须去手动添加对应形式的super语句。
Super语句也必须放在构造函数的首行。
多态
多态的形式:1.函数的重载
2.函数的重写
3.向上造型:用父类声明对象而用具体子类去创建对象
函数的重写、向上造型 ---- 基于继承关系
根据绑定时期不同,分类为:{
行为多态:函数的重载和重写
对象多态:向上造型
}
根据时期不同,分类为:{
编译时多态:函数的重载(在编译时期就已经确定绑定哪种形式)
运行时多态:函数的重写
运行时多态:向上造型:Animal a = new Lion() --- 在编译时期不会关心 是哪个具体子类;在运行的时候需要确定是哪个具体子类才能分配空间
}
注意:向上造型用父类声明子类初始化,由于使用父类声明,所以对象 能够干什么需要看父类(也就是说能使用什么方法,父类有的才行);由于使用子类初始化,具体怎么用要看子类(也就是执行子类的代码,具体要看情况才行)。
对于重写的理解
1、子类中重写的方法的权限修饰符要大于等于父类方法的权限修饰符
错误代码:
class A {
public void m(){}
}
class B extends A {
void m(){}`
}
解析:
A a = new B(); // 声明a对象的时候用的A类,那么A类告诉a对象可以使用一个m方法,并且告诉a对象这个m方法是一个public修饰的方法在任何地方都可使用
m(); // 具体的实现用的是B这个子类,当执行方法的时候需要看具体子类,发现这个方法使用默认权限修饰的 ,报错
2、如果父类方法的返回值类型是引用类型,那子类的方法的返回值类型是父类或者父类方法返回值类型的子类
错误代码:
class A {
public void ma(){}
}
class B extends A {
public void mb(){}
}
class C {
public B m(){/*more code here*/}
}
class D extends C {
public A m(){/*more code here*/}
}
解析:
C c = new D(); // 声明c对象用的是C类,C类告诉c对象有一个m方法可以使用,并且这个m的方法的返回值类型是一个B类型
B b = c.m(); // 方法的具体执行要看子类,子类中实际上这个方法的返回值类型是A---子类对象接了一个父类对象
b.mb(); // 子类接了一个父类对象,所以实际上是一个父类对象,而父类对象中不一定含有子类中独有的方法 --- 错误的
3、如果父类中方法的返回值类型是基本类型/void/最终类,子类方法的返回值类型必须一致
解析:基本类型之间没有继承关系
#静态
作用:修饰变量、方法、代码块、内部类
方法区
分为静态常量池和静态区。
存储类信息。存储类的基本信息的区域称之为静态常量池
存储静态属性和静态方法的地方称之为静态区
一个类在第一次正正使用的时候才会向方法区中加载,所以类只加载一次,加载完成之后就不再从方法区中移除。(只进不出)
静态变量(类变量)
用static修饰的变量称之为静态变量或者是类变量。静态变量是随着类的加载而加载到方法区中的静态区,并且在静态区中自动赋予了默认值。每一个对象实际上是引用了这个静态变量在静态区中的地址,静态变量是被所有的是对象所共享的。静态变量是先于对象而存在的,所以 静态变量可以不通过对象而通过类名来调用。
System.out System.in 静态的
注意:静态变量只能定义在类中
静态变量可以定义在构造代码块中吗? --- 不可以
静态变量可以在构造代码块中初始化吗? --- 可以
静态变量内存分析图
静态方法
用static修饰的方法称之为静态方法,也叫类方法。静态方法随着类的加载而加载到方法区中的静态区,并不执行,而是在被调用的时候去栈内存中执行。静态方法也是先于对象 而存在的,所以静态方法可以不通过对象而是通过类名来调用。
System.arraycopy(); Arrays.tostring(); arrays.copyof(); math.sqrt()
System.out.print( ) ---- out是一个静态对象
注意:静态方法中可以定义静态变量吗? --- 不可以,静态方法只是加载到静态区,在调用的时候到栈内存中执行,在执行的时候才会给其中的变量开辟空间存储。
静态方法中不可以直接调用本类的非静态方法(非静态方法只能通过对象调用,而静态方法在类加载的时候加载到静态区)
在主函数中可以使用this么?-不可以--也不能用super
静态里面 没有this 没有super
静态方法可以重载(重载只关心参数列表,不关心修饰符)
静态方法不可以重写
父子类中可以存在方法签名完全一致的静态方法么?--可以--但是不构成重写--但重写的规则对它依然适用
静态方法看的是声明类,非静态看的是实现类
静态不属于对象而是属于类的
父子类中存在了方法签名一致的方法的时候,要么两者都是静态,要么两者都是非静态
静态代码块
用static{}包括起来的代码。在类加载的时候到栈内存中执行。
执行顺序:父类静态 ->子类静态 -> 父类非静态 -> 子类非静态;
#final
final 可以修饰数据,方法,类
final修饰变量:
final 修饰某个量的时候,这个量称之为常量(这个常量是手动定义的)
final修饰的变量,定义后不可改变
对于基本数据类型,指的是值不可改变;引用数据类型指的是地址不可改变
可以使用static修饰final --- static final int i= 2;
final修饰方法:
final修饰的方法不能被重写
final不能被重写,可以被继承
final修饰的方法可以被重载 (重载只看函数签名,与其他修饰符无关)
final修饰类 —— 最终类(特点:不可被继承)
现阶段,最终类中的方法无法被重写。
#abstract
当子类中有一些共有的但是细节不同的行为的时候,可以对这些行为进行抽取,将这些行为抽取到父类中以抽象方法的形式展现。
注意:抽象方法所在的类一定是抽象类。
子类必须实现(重写)父类中的抽象方法。
抽象类中可以有抽象方法,也可以没有。
抽象类不能实例化,但是它有构造方法。
抽象类和普通类的区别:
抽象方法可以重载。
public abstrace void eat();
public abstrace void eat(String s);
抽象方法和和非抽象方法重载
public abstrace void eat();
public void eat(int count){
System.out.print(“吃了” + count);
}
抽象方法不可以使用static修饰
抽象方法不可以使用final修饰
抽象方法不可以使用private修饰
抽象方法可以使用默认权限修饰 前提:父子类必须同包
#接口
用interface定义接口,用implements关键字让类和接口产生联系,这种联系称之为实现。
接口不能用于实例化,也没有构造函数——接口不是类。
接口会在编译时声称一个class文件,但它不是类
单继承,多实现——一个类只能有一个父类,但可以有多个接口。
在接口中的方法默认是用abstract修饰——JDK1.8以前
Jdk1.8以后接口中可以定义实体方法
在接口中所有的方法默认使用public修饰,而且只能是public
接口中的方法默认是抽象方法,接口中的量默认是静态常量,默认使用public static final 修饰(修饰词之间没有优先级,在前在后都可以)
接口可以继承接口——接口之间是多继承的
抽象类在实现接口后一定要实现这个接口中所有的抽象方法吗?——不一定
接口在一定程度上统一结构,实现方法的统一调度,但是可能会导致代码的冗余
#内部类
方法内部类
定义在方法中的类
注意:
package cn.tedu.inner;
import java.io.Serializable;
import cn.tedu.abstractx.AbstractDemo;
public class InnerDemo1 {
public static void main(String[] args) {
Outer1 o1 = new Outer1();
o1.m();
}
}
class Outer1 {
int k = 10;
public void m() {
System.out.println("Outer1 m()");
final int x = 6;
// 方法内部类
// 只能在定义它的方法中使用
// 方法内部类中可以定义成员属性和成员方法
// 不允许定义静态变量和静态方法
// 方法内部类中允许定义静态常量
// 方法内部类可以使用外部类中成员属性和静态属性,但是不能用当前方法中的局部变量
// 方法内部类可以使用当前方法中的常量
成员内部类
定义在类内方法外的类
注意:
package cn.tedu.inner;
public class InnerDemo2 {
public static void main(String[] args) {
Outer2 o2 = new Outer2();
System.out.println(o2.i);
//成员内部类在外部类外创建对象的时候需要依赖于外部类对象
Outer2.Inner2 oi2 = o2.new Inner2();
oi2.m();
new Outer2().new Inner2().m();
}
}
class Outer2 {
int i = 5;
// 成员内部类
// 可以定义成员属性和成员方法
// 不允许定义静态变量和静态方法
// 可以定义静态常量
// 可以使用外部类中的成员属性和静态属性,以及方法
// 目前为止只要一个修饰符可以作用在属性上就可以作用在这个 成员内部类上
class Inner2 {
static final int j = 9;
public void m() {
ma();
System.out.println("inner m" + i);
}
}
private void ma() {
System.out.println("outer m");
}
}
静态内部类
用static修饰的类
package cn.tedu.inner;
public class InnerDemo3 {
public static void main(String[] args) {
Outer3.Inner3 oi3 = new Outer3.Inner3();
oi3.m();
new Outer3.Inner3().m();
Outer3.Inner3.m2();
System.out.println(Outer3.Inner3.j);
}
}
class Outer3 {
static int k = 8;
// 静态内部类
// 可以定义成员属性和成员方法
// 可以定义静态属性和静态方法
// 不允许使用外部类中的非静态属性和非静态方法
// 可以使用外部类中的静态属性和静态方法
static class Inner3 {
int i = 5;
static int j = 6;
public void m() {
System.out.println("inner m");
}
public static void m2() {
System.out.println("inner m2");
}
}
}
匿名内部类
匿名内部类本质上是继承了是对应的类或者实现类对应的接口。
如果匿名内部类定义在了方法或者语句内,使用规则和方法内部类一致
package cn.tedu.inner;
public class InnerDemo4 {
// 当利用接口创建匿名内部类的时候,实际上是实现了对应的接口
C c = new C() {
};
@SuppressWarnings("unused")
public static void main(String[] args) {
// 匿名内部类实际上是继承了对应的类
// 如果匿名内部类定义在了方法或者语句内的时候,使用的规则和方法内部类一致
A a = new A() {
};
// B b = new B() {
// };
}
}
abstract class A {
}
final class B {
}
interface C {
}
扩展:内部接口——类中的接口默认是静态的;接口中的类或接口都是用static修饰的
Class Outer{ //内部接口,默认使用static修饰 不能加其他修饰词
Interface Inner{
}
}
Interface Outer1{ //接口中的内部类默认使用static修饰
Class Inner1{
}
}
Interface Outer1{ //接口中的接口默认使用static修饰
Interface Inner1{
}
}
#包
声明包用的是package,导包用的是import
在Java中规定,一个Java文件中只能有一个package语句,而且package语句必须放在Java代码的首行。
import java.util.* ;—— 表示通配符,表示导入当前包下的所有类但是不包括子包下的类。
java.util.Scanner 包名+类名 -> 类的
同包类不需要导包
常用包
java.lang —— 包含了一个Java程序运行的一些基本类,在程序启动之后会自动的加载到方法区——不需要导包
java.util —— 包含了很多工具类
java.math —— 数学运算
Java.io —— 数据传输
Java.net —— 网络通信
Java.nio —— 高并发