一、JAVA概述
1. 基础
javac = java compiler(java编译器)
JVM(java virtual machine虚拟机)< JRE(java Runtime Environment运行环境)<JDK(java Development Kit 开发工具包)
java Hello.class会报错,因为会认为运行的是java.class这个类,直接java class即可
原始文件.java被称为源文件;javac编译后的文件.class被称为字节码文件
一个源文件只能有一个public类,且这个类名要和文件名一致。其他类的数量不限,编译的时候每个类都会生成一个class文件,其他类里也可以写main函数,只要运行指定类即可
2. 转义字符:
public class ChangeChar{
public static void main(String[] args){
// \t是制表符 对齐
System.out.println("上海\t北京\t成都\t深圳");
// \n是换行,光标到下一行
System.out.println("红楼梦\n三国演义\n西游记");
// \"\'实现双单引号
System.out.println("我说\"这绝不可能\",你说:\'不一定\'");
// \\是单斜杠 \\\\是双斜杠 一个\转移后面一个斜杠
System.out.println("C:\\D:\\\\E");
// \r是回车,光标回到该行最前面 开始覆盖
System.out.println("回车是替换掉该行前面的字\rHuiC");
}
}
3. 注释
多行注释里面不能嵌套多行注释
文档注释:
/**
* @author lixianzhuang
* @version 1.0
*/
@的名词必须是指定的那几个,利用
javadoc -d D:\\temp\ -author -version ChangeChar.java
其中,D:\temp\是要生成的index网页存放的地址, -author -version是所@的字段,编辑的文件是源文件,而非字节码文件,打开temp下面的index.html文件,可以看到如图:
4. 常见错误
- 编译或者运行时,找不到文件 javac Hell0.java, 把文件名或者目录找对
- 主类名和文件名不一致 , 修改时保持一致即可
- 缺少;
- 拼写错误,比如 1 -> l 0->0, void -> viod , 要求写代码时,一定要小心
二、变量
1. +号的使用
2. 数据类型
整型默认int(空间节省4字节),声明long,数字后面必须加L(或l)
因为浮点型尾数部分可能丢失,造成精度损失,所以小数都是近似值
浮点型默认是double型(因为精度更高),声明float型号要加f或F
java中null只能被装换成引用类型,所以不能把null的值赋给以上的几种基本数据类型
int a = 10L // 错误,因为大数10L不能放进小int里
double b = 1.1F // 正确,因为小数1.1F可以放进大double里
浮点数使用陷阱:
所以含有运算的浮点数进行判断时应该让两个数的差值的绝对值小于某个精度。
如果是查询到的小数或者是直接赋值,则可以判断相等。
字符型
char 可以直接放数字,但是表示的是数字的ASCII码
char c = 97;
打印出来是a,而不是97
字符类型都需要用单引号引起来,使用了双引号表示的是字符串
char字符本质存的就是字符所对应的Unicode数字,默认输出是字符,也可以进行数据类型转换输出数字
char c = 'a';
System.out.println((int)c);
输出是97
字符加法:
因为字符存的是数字,所以相加是存放的数字相加
如果相加后的数字还是存为char类型,那就默认输出ASCII码,否则就是单纯的数字相加。
char本质存放的就是字母对应的ASCII码数字
双引号的字符串相加才是拼接!
JAVA的布尔类型只能用true和false,不能用数字,这点不同于C
PS:JAVA中字符串不能用==比较是否相等,==比较的是地址,应该用a.equals(b)来判断ab字符串是否相等。
最好是把具体的字符串作为a,不具体的作为b,可以避免空指针
但是字符型可以直接用==判断相等,反而不能用equals
3. 数据类型转换
a. 自动数据类型转换
相互计算,大包容小
不同类型变量在计算的时候会先变成最大的类型,再计算,所以n1+1.1(1.1默认类型是double)的和的数据类型是double,所以定义float类型会出错。只能用大数据类型接收小的
byte直接看数值大小
char,short,byte三个类型不能自动转换,但是可以计算,先统一转化为int类型
即使是byte+byte,结果也是int类型!!!
b. 强制类型转换
大---->小,有数据损失
其他类型强制转字符串,只需要直接+“”即可
字符串类型转其他,需要调用包装类,不能直接(int)转
三、运算符
1. 算术运算符
除法
JAVA没有整除这个专门的语法,除法结果是小数还是整数完全取决于参与运算的数据类型。
double b = 10 / 4 //结果是2.0 10和4产生结果2 2是double类型 所以结果2.0
b = ++a // 先++再赋值
b = a++ // 先赋值再++
面试题
区别在于先赋值还是先++,先赋值会先调用一个临时变量tmp存放原始值,先++则是先+1再调用临时变量
2. 关系运算符
instanceof 是否是某个类的对象
3. 逻辑运算符
和关系运算符一样,结果都是Boolean值
&&短路与:如果第一个条件为 false,则第二个条件不会判断,效率高
& 逻辑与:不管第一个条件是否为 false,第二个条件都要判断,效率低
a^b:ab异或
int x = 5;
if(x++ == 6) // 先比较再++,所以是False
4. 赋值运算符
复合赋值运算符会自动进行类型转换
byte b = 2;
b += 3;// 相当于 b = (byte)(b + 3)
b++; // 相当于 b = (byte)(b + 1)
都不会报错
5. 三元运算符
条件?表达式1:表达式2;
条件为真,返回表达式1(一真大师)
6. 运算符优先级
7. 标识符命名规则和规范
规则:
只能由英文字母,数字,_ 和$这俩特殊字符组成
规范:
1)包名:多单词组成时所有字母都小写:aaa.bbb.ccc //比如 com.hsp.crm
2) 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz [大驼峰]
比如: TankShotGame
3) 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz [小
驼峰, 简称 驼峰法]
比如: tankShotGame
4) 常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
比如 :定义一个所得税率 TAX_RAT
8. 进制(很重要的基本功)
8进制是0开头;16进制是0x开头
十进制转二进制:
八进制和十六进制转二进制同理
二进制转八进制/十六进制:
从低位开始,将二进制数每三/四位一组,转成对应的八进制/十六进制数即可
八进制和十六进制转二进制反过来即可。
9. 反码补码原码
七八条尤为重要:进行位运算的时候,先都转化为补码进行运算,然后将结果变为原码进行输出!
四、控制结构
1. Switch语句
如果没有break,会直接继续到下一个case的语句,而不判断下一个case的表达式是否满足,直至遇到break或者运行完switch语句,这种现象称为穿透
2. for循环
- 循环条件是返回一个布尔值的表达式
- for(;循环判断条件;) 中的初始化和变量迭代可以写到其它地方,但是两边的分号不能省略。如果需要调用i,则需要在for循环外面定义i,所以有时候不会把初始化和迭代写在for内
- 循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代语句,中间用逗号隔开。
for循环的执行顺序
先初始化,再判断条件,再执行语句,最后再进行变量迭代(自增等)
3. 多重循环
break细节
continue跳出本次循环。
注意:continue跳出循环时,循环初始化不会重置,实际是跳到下一次循环的循环条件那里。
return 表示跳出所在的方法,如果 return 写在 main 方法,直接退出程序
五、数组、排序和查找
1. 数组
double[] hen = {1,2,3,4,5}//中括号即可以这样写,也可以写在hen后面,两种写法等价
数组长度hen.length 无括号
二维数组声明三种形式
int[][] arr / int arr[][] / int[] arr[] 注意第三种也可以的
举例:int[] x, y[]
x是一维数组,y是二维数组
数组判断:
1. String[] str = new String[]{"1", "2", "3"}; // 对 可以这样初始化
2. String[] str = new String[3]{"1", "2", "3"}; // 错 这样初始化不能加长度
3. String[] str = new String[]{'1', '2', '3'}; // 错,不能把char给String,这俩不是int->double的包容关系
数组初始化:
一维
动态1: double scores[] = new double[5];//数据类型 数组名[] = new 数据类型[数组长度]
动态2: double scores[];
scores = new double[5];// 先声明后分配空间
静态: double scores[] = {1,2,3}; // 知道所有值
动态方法别忘了加new
二维
动态1:类型 数组名[][] =new 类型[大小][大小]
动态2:先声明后分配地址 int arr[][];
arr = new int[3][4];
动态3:列数不确定 int arr[][] = new int[3][];
先空着一维数组长度,后面必须再分别给一维数组开空间
静态: double int[][] = {{1,2,3}, {2,3,4}} // 两个中括号
数组注意细节
- 数组是多个相同类型数据的组合,实现对这些数据的统一管理
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用。
- 数组创建后,如果没有赋值,有默认值
int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,boolean false,String null - 使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值 3 使用数组
- 数组的下标是从 0 开始的。
- 数组下标必须在指定范围内使用,否则报:下标越界异常
数组赋值(*)
不同于变量赋值,数组赋值是地址赋值,新数组的改变会导致旧数组跟着发生改变
地址赋值也叫引用赋值,引用传递,引用拷贝
变量存在栈里,存的是值;
数组存在堆里,存的是地址。
arr[i] 和arr[j]互换数值需要使用临时变量,不能像python直接换
数组扩容比较麻烦,每次需要重新开辟空间,链表扩容则更方便
二维数组的内存形式
二维数组的每个元素里放的是一维数组的地址,再通过每个地址获取基本元素
2. 排序
冒泡排序
冒泡排序一共要进行(n-1)次循环,每一次循环都要进行当前n-1次比较
所以一共的比较次数是:
(n-1) + (n-2) + (n-3) + … + 1 = n*(n-1)/2;
所以冒泡排序的时间复杂度是 O(n2)
六、面向对象(基础)
1. 类与对象
类就是一种数据类型;对象是一个类的实例
类中:属性=成员变量=字段,三种说法等价
属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
访问修饰符: 控制属性的访问范围
有四种访问修饰符 public, proctected, 默认(不写即默认), private
对象在内存中存在形式(*)
- 对象和数组一样,都是引用类型,指向一个地址
- 对象中的属性和行为如果是基本数据类型,直接存放在堆里,否则指向方法区的常量池的地址
- new一个对象时,会把这个类的信息加载到方法区(一个类只会加载一次)
类和对象的内存分配机制(*)
Java 内存的结构分析
- 栈: 一般存放基本数据类型(局部变量)
- 堆: 存放对象(Cat cat , 数组等)每次new都会在堆里产生一个新的对象
- 方法区:常量池(常量,比如字符串), 类加载信
方法调用机制
return之后,临时开辟的getSum栈空间被销毁;
方法里面不能嵌套方法
方法传参机制(*)
基本数据类型进行的是值拷贝,形参的任何变化都不会影响实参
引用数据类型是地址传递,可以通过形参影响实参(对象也是引用数据类型)
2. 递归
3. 方法重载
java 中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!
构成方法重载就两个要素:1方法名相同;2形参结构不同
实参匹配时,优先匹配类型完全一致的,若无,再匹配类型能自动转换的方法
4. 可变参数
基本语法:
访问修饰符 返回类型 方法名(数据类型... 形参名) {}
直接把nums当做数组处理即可。
5. 作用域
6. 构造器/构造方法
[修饰符] 方法名(形参列表){
方法体;
}
注意点
- 构造器的修饰符可以默认, 也可以是 public protected private
- 构造器没有返回值,也不写void
- 方法名 和类名字必须一样
- 参数列表 和 成员方法一样的规则
- 构造器的调用, 由系统完成
- 构造器的存在,可以在new的时候就传入信息,例如Person p1 = new Person(“smith”, 80);
构造器细节
含构造器的对象创建流程分析
构造器初始化是对象初始化中最后执行的,所以构造器的输入内容,是最终的初始化内容
7. this
哪个对象调用this,this就指代哪个对象
- this 关键字可以用来访问本类的属性、方法、构造器
- this 用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一条语句)
- this 不能在类定义的外部使用,只能在类定义的方法中使用。
8. 本章习题
七、面向对象(中级)
1. IDEA
常用快捷键
- 删除当前行, 默认是 ctrl + Y 自己配置 ctrl + d
- 复制当前行, 自己配置 ctrl + alt + 向下光标
- 补全代码 alt + /
- 添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消注释】
- 导入该行需要的类 先配置 auto import , 然后使用 alt+enter 即可
- 快速格式化代码 ctrl + alt + L
- 快速运行程序 自己定义 alt + R
- 生成构造器等 alt + insert ,按住CTRL再点击可实现多选
- 查看一个类的层级关系 ctrl + H [学习继承后,非常有用]
- 将光标放在一个方法上,输入 ctrl + B , 可以定位到方法或原码 [学继承后,非常有用]
- 自动的分配变量名 , 通过 在后面加 .var [老师最喜欢的]
- ALt+enter报错代码弹出修改提示
模板
2. 包
包的本质就是创建不同的文件夹来保存类文件
常用的包:
- java.lang.* //lang 包是基本包,默认引入,不需要再引入.
- java.util.* //util 包,系统提供的工具包, 工具类,使用 Scanner
- java.net.* //网络包,网络开发
- java.awt.* //是做 java 的界面开发,GUI
3. 访问修饰符
- 公开级别:用 public 修饰,对外公开
- 受保护级别:用 protected 修饰,对子类和同一个包中的类公开
- 默认级别:没有修饰符号,向同一个包的类公开. 4) 私有级别:用 private 修饰,只有类本身可以访问,不对外公开.
4. 封装(*)
封装与构造器结合:在构造器中set
5. 继承(*)
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去间接访问
- 子类必须先调用父类的构造器, 完成父类的初始化,再执行子类构造器(先有父才能有子)
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会先调用父类的无参构造器(即有一个默认的super()),如果父类没有提供无参构造器,则必须在子类的构造器中用 super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(父类有无参构造器,可以什么都不写,否则必须super(参数1,参数2)去指定一个有参构造器)
- 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
- super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
- super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java 所有类都是 Object 类的子类, Object 是所有类的基类.
- 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)(构造器执行顺序:顶级父类object—>下一级父类—>下下级父类—>…—>main函数执行的类)
- 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】 - 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
11)子类构造器第一行只要没有this,就默认有一个super()先去调用父类无参构造器
继承的本质(**)
查找关系
如果age是私有,但是爷爷类里的age是公有,还是会直接报错,不会说是遇到父亲类是私有就继续找爷爷类,不会这样,会直接报错
输出 a \t b name \t b
要注意的点:
- 有this就没有super,所以b类的第一个默认构造器没有super指向a类,反而是this先指向下面的有参构造器
- b类第二个构造器有个默认的super()
super关键字
super和this语法很像,只是this是指代本类,super是指代父类
方法重写/覆盖
重写和重载的比较
6. 多态
方法就看运行类型,属性就找编译类型
对象的多态(*)
向上转型:
不能调用子类中的特有成员是因为在编译阶段能调用哪些成员是由编译类型决定的
运行的时候跟继承一样,先从子类开始找方法,再逐次往父类开始找
向下转型(因为向上转型不能调用子类的特有成员,所以可以使用向下转型去寻找)
示例:
Animal animal = new Cat();
Cat cat = (Cat) animal;
第三条:即原来的引用就是cat,本身就是cat才能强转cat
属性没有重写之说!属性的值看编译类型
方法才是从下往上查找
instanceOf
a instanceOf XX //该语句不含括号 XX不是写在括号内的
比较操作符,用于判断a对象的运行类型是否为 XX 类型或 XX 类型的子类型
动态绑定机制(**)
1) 执行方法时,看运行类型是哪个类,就去哪个类找方法,若无,依次往上找父类中的方法。若父类中的方法需要调用方法2,仍先去运行类型类中找方法2,找不到再依次往上,依次类推。即每次找方法,都要先在运行类型的这个类空间里找
2) 调用属性时,就近找就完事了
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
若要调用子类特有的方法,就向下强转使用
多态参数
形参是父类型,实参是子类型
7. Object类
==和equals的区别
interger和string子类中的equals都是判断内容是否相等
toString
默认返回:全类名+@+哈希值的十六进制
但一般都不用默认,一般都是用于打印对象或拼接对象,Alt+Insert可以直接调出新的约定的toString方法
当直接输出一个对象时,toString 方法会被默认的调用, 比如System.out.println(monster); 就会默认调用monster.toString()
8. 断点调试
在调试过程中,是运行状态,所以执行的是对象的运行类型
F7(跳入方法内) F8(跳过) shift+F8(跳出) F9(resume,执行到下一个断点)
F7:跳入方法内
F8: 逐行执行代码. shift+F8: 跳出方法
八、面向对象(高级)
1. 类变量和类方法
总结:静态只能访问静态成员,不能访问super和this,非静态都可以访问,都要注意访问权限
类变量
类变量的访问修饰符权限同变量的修饰符权限一致
因为类加载,类变量就创建了,所以类.类变量名就可以使用,无需先实例化类
类方法
2. main方法
- 在 main()方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性。
- 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
IDEA给main传参数
有个program arguments
填入运行即可
3. 代码块
- 普通代码块只跟创建对象有关系,创建几次执行几次,跟类是否加载毫无关系(静态代码块看加载,且只执行一次;普通代码块看创建对象,有几次来几次)普通代码块可以视为构造器的补充,构造器被调用它才会跟着被调用,而加载类是不调用构造器的,只有创建对象会调用构造器
静态一定都在普通前面,不管是父类还是子类的静态(因为是先进行类加载再创建对象,加载阶段静态的就开始工作了,而普通的要等到后续创建对象才行)
静态代码块本质也是静态方法,只能调用静态成员
4. final
final可以修饰形参,但是这个形参不能再改动了
不能被继承就很难使用,这个方法就没意义了
5. 抽象类
因为抽象方法是必须要被子类重写的,但这三个关键字都不允许重写
6. 接口
接口可以视为一个更加抽象的抽象类
即jdk8后,接口中可以含有default,static,abstract三种方法
第七条,实现接口的类也都可以使用接口中的属性
7. 内部类
如果定义类在局部位置(方法中/代码块) :(1) 局部内部类 (2) 匿名内部类
定义在成员位置 (1) 成员内部类 (2) 静态内部类
1. 局部内部类
第七条:Outer02.this 本质就是外部类的对象, 即哪个对象调用了 m1, Outer02.this
2. 匿名内部类
匿名内部类可以当做实参直接传递,简洁高效
- 匿名内部类会由系统分配一个名字(外类+$1(这个按顺序编号,是第几个匿名内部类就是$几))(匿名不是无名,就像QQ匿名模式会分配给你一个随机名字一样)。匿名内部类只用一次,且因为语法有new,每次使用都伴随着对象实例的创建
- 相比于创建对象的语法,匿名内部类多了一对括号,结尾别漏分号,因为是个语句
- 匿名内部类的形参列表会传递给构造器