第1章 初识java
java文件 程序员认识 计算机不认识 只认识0和1
javac命令 编译 将java文件编译成.class文件
java命令 运行 输出结果
第2章 数据类型
数据类型: 1、基本数据类型 byte 1字节 short 2字节 int 4字节 long 8字节
float 4字节 double 8字节
boolean 1字节 char 2字节
2、引用数据类型 除了基本的数据类型 剩下的数据类型都是引用数据
定义变量: 数据类型 变量名 = 值
数据类型之间的转换: 他们之间能进行转换
自动类型转换: 占用空间小的类型可以自动向占用空间大的类型进行转换
强制类型转换: 占用空间大的类型可以自动向占用空间小的类型进行转换
目标类型 变量名 = (目标类型)变量值
第3章 运算符
算数运算符: + 和 /
当加号左右两边都是数字类型时,加号才是数学意义中的加号
当加号左右两边有一边为字符串时,加号是连接符的作用,最终的结果还是字符串
/:当除数和被除数都是整数时,结果只取到小数部分中的整数部分
当除数和被除数有一个为浮点类型数据时,结果是整数部分+小数部分
比较运算符: 比较后的结果为boolean类型
java 中比较两个数是否相等: == = 赋值运算符
javaScript 脚本语言 === ==
赋值运算符: += -= /= *= 默认包含了强制类型转换 byte a = 1; a+=1;// a =(byte)(a+1);
自增运算符: ++ --
1、当变量不参与其他运算时 ++或者--在前或者在后效果是一样的 都是让变量加1或者减1
2、当变量参与其他操作时 ++或者--在前或者在后效果是不一样的
++或者--在前边时 先执行++或者--的操作 然后再执行其他的操作
++或者--在后边时 先执行其他的操作 然后再执行++或者--的操作
逻辑运算符: 要求运算符前后的值必须是boolean类型 而且最终的结果也是boolean类型
& 当运算符前后的结果都是true 整个结果才是true,有一边为false,整个结果为false
| 当运算符前后的结果都是false 整个结果才是false,有一边为true,整个结果为true
! 取反 将表达式的结果进行取反 !true false !false true
^ 异或 表达式两边的值相同时 结果为false 不同时为true
短路逻辑与 && 当运算符前后的结果都是true 整个结果才是true,有一边为false,整个结果为false
短路逻辑或 || 当运算符前后的结果都是false 整个结果才是false,有一边为true,整个结果为true
不同点:
&和&& & 当前面的结果为false时,后面表达式依然执行
&& 当前面的结果为false时,后面表达式不执行
|和|| | 当前面的结果为true时,后面表达式依然执行
|| 当前面的结果为true时,后面表达式不执行
三元运算符: 表达式?表达式1:表达式2 当表达式的结果为true时,执行表达式1 否则执行表达式2
第4章 分支机构
1、if(条件表达式){ //条件表达式的结果为boolean类型 可以写逻辑运算 可以写关系运算
代码块 当表达式的结果为true时,执行if中的代码块,当表达式的结果为false时,则不执行
}
2、if(条件表达式){ 当表达式的结果为true时,执行if中的代码块,
当表达式的结果为false时,执行else中的代码块
代码块1
}else{
代码块2
}
3、if(条件表达式1){ 先判断条件表达式1, 如果结果为true 执行对应的代码块 后面的判断都不执行了
代码块1 如果结果为false 继续后面的判断 判断流程和上述一致
}elseif(条件表达式2){ 当所有的表达式的结果都是false 则执行else中的代码
代码块2
}elseif(条件表达式3){
代码块3
}...else{ else 可以有 也可以没有
代码块n
}
4、switch
switch(表达式){ 注意:case后面的值不能重复 default 可以放在中间 只能有一个
case 值1:
代码块;
[break];
case 值2:
代码块;
[break];
case 值3:
代码块;
[break];
....
default: 可以有 也可以没有
代码块;
[break];
}
执行过程: 1、先计算switch后面的表达式的值
2、依次和case后面的值进行对比,如果对比成功,执行对应的代码块,什么时候遇到break,什么时候终止,
如果没有遇到,执行下一个case中的代码,再次判断是否遇到break。
3、如果表达式的值和所有case后面的值都不相等,则执行default中的代码
第5章 循环结构
1、for循环
基本: for(循环变量初始化;循环条件判断;改变循环变量){
循环体;
}
1、循环变量的初始化
2、循环条件的判断,当判断的结果为true,执行循环体,执行第三步
当判断结果为false时,结束循环
3、改变循环变量 再次执行第二步
2、while
初始化循环变量;
while(循环条件判断){
循环体;
改变循环变量;
}
1、初始化循环变量
2、循环条件的判断, 如果判断为true,执行第三步
如果判断为false,结束循环
3、执行{}中的代码,在执行第二步
3、do...while
初始化循环变量;
do{
循环体;
改变循环变量;
}while(循环条件判断);
1、初始化循环变量
2、执行do中的代码
3、循环条件的判断 如果判断结果为true,执行第二步, 如果判断结果为false 则结束循环
4、循环辅助语句
break: 结束当前整个循环,后面的循环也都不执行了
continue: 结束当前某一次循环,后面的循环依然执行
return: 结束方法;返回结果
第6章 数组
引用数据类型
一块连续的内存空间,并且可以存储多个元素
获取元素根据数组的下标获取:
数组的下标从0开始 数组下标的最大值=数组的长度-1
数组的长度:数组名.length
如何定义数组:
1、数据类型[] 数组名 = new 数据类型[数组的长度]
2、数据类型[] 数组名 = {元素1,元素2..}
3、数据类型[] 数组名 = new 数据类型[]{元素1,元素2..}
缺点: 一旦数组的长度确定了,当添加的元素超过数组的长度,会报数组下标越界异常。
ArrayIndexOutOfBoundsException
某个位置添加元素: 数组名[索引] = 值
获取元素: 数组名[索引]
第7章 方法
1、如何定义方法
public static 返回值类型 方法名(参数列表){
如果有返回值 return 返回值
没有返回值 不用写return
}
1、有参无返回值
public static void 方法名(参数列表){ 参数: 数据类型 变量名 形参
方法体;
[return;]
}
2、有参有返回值 返回的结果的类型必须是返回值的类型
public static 返回值类型 方法名(参数列表){ 参数: 数据类型 变量名 形参
方法体;
return 返回的结果;
}
3、无参无返回值
public static void 方法名(){ 参数: 数据类型 变量名 形参
方法体;
[return;]
}
4、无参有返回值 返回的结果的类型必须是返回值类型规定的类型
public static 返回值类型 方法名(){ 参数: 数据类型 变量名 形参
方法体;
return 返回的结果;
}
2、方法的调用:
返回值类型 变量名 = 方法名(实参列表)
3、实参和形参的区别:
形参:在方法定义的时候创建的参数
实参:在方法调用的时候传入的参数
1、实参的个数和形参的个数一致
2、实参对应位置的参数的数据类型和形参对应位置的参数的数据类型保持一致
4、参数的传递
基本数据类型的参数: 对形参的改变,不影响实际的值
引用数据类型的参数: 对形参的改变,会影响实际的值
5、方法的重载
方法名相同,参数的个数或者类型不相同,便形成了重载,跟方法是否有返回值和参数的名字无关。
6、递归调用
方法自己调用自己
1、递归一定要有出口 在合适的时机结束方法
2、即使有出口,调用的次数也不能过多
错误: StackOverflowError
第8章 面向对象
1、面向对象的三大特征: 封装 继承 多态
2、1、类 模板 属性 : 类有什么特征 数据类型 变量 = 【值】
行为 : 类能干什么 方法 将方法中的static关键字去掉
2、对象 类名 对象名 = new 类名(参数) new的后面的跟的是构造方法
对象的创建是需要构造方法执行完毕后,才有对象吗?不是
当new关键字执行的时候就有对象了,创建完成后,对象中的属性都是默认值
使用构造方法对默认值进行初始化操作
3、构造方法 也可以重载
如果在类中我们没有显示的创建一个构造方法,那么jvm会默认创建一个无参的构造方法
语法: public 类名(形参列表){
}
如果在类中我们显示的创建了一个构造方法,那么jvm就不会给我们创建默认的了,我们在创建对象
的时候,只能使用我们自己创建好的构造方法了
4、this关键字
变量的分类:
局部变量: 在方法中定义的变量 称之为局部变量
栈帧中的局部变量表位置,栈帧没有了,局部变量也就消失了
全局变量: 在类中定义的变量 称之为全局变量 属性/全局变量/成员变量
跟对象保存在一起,放在堆中 对象被回收了 全局变量也就没有了
局部变量和全局变量重名:
调用方法时,优先访问的是局部变量.
想访问全局变量: 【将其中一个变量改名】
this关键字指带的是当前的对象
【this指代是new关键字执行的时候所开辟的那块堆内存】
this除了上述的功能,还可以显示的调用某个构造方法
this(实参) 根据参数的个数和类型调用对应的构造方法
5、封装
1、将属性私有化,在属性的前面加上一个访问权限修饰符private 因为private修饰的成员方法/变量只能在本类中访问
2、提供set/get方法 set方法给属性赋值 get获取属性值
提高了代码的安全性
6、访问/权限修饰符 private 默认的 protected public
第9章 继承
在java中,只支持单继承,一个类只能有一个父类,如果没有显示的声明一个类的父类,那么默认的这个类的父类是Object
但是java支持多层继承,
继承: class A extend B 子类就拥有了父类中非私有的属性和方法
子类对于继承来的属性或者方法不满意时,可以进行重写,重写时和父类的方法保持一致。
当再次调用时,调用的是重写后的方法。
super关键字:
可以使用super关键自调用父类中的属性或者方法,super.属性 super.方法
调用子类的构造方法之前,先调用父类中的构造方法,如果想在子类中显示的去调用父类的某一个构造方法,
super(实参列表)
final: 最终的
属性: 代表这个属性是一个常量
基本数据类型: 变量中的值不能改了
引用数据类型: 变量所对应的内存地址不能改了,但是内存地址所对应的内容是可以改的
方法: 最终的方法 这个方法就不能被重写了 但是可以重载
类: 最终的类 这个类就不能被继承了
static: 静态的 类加载的时候 就进行加载了 仅仅加载一遍 跟类保持在同一个声明周期
属性: 静态属性 被当前类创建的对象 共享该属性 非static修饰的 是每个对象私有的
方法: 静态方法 1、对象名.方法名 2、类型.方法名
静态方法: 只能访问静态的属性或者方法
非静态方法: 无论是静态的还是非静态的都可以访问
1、如果一个对象变成垃圾对象,被垃圾回收器回收了,那么它其中静态类型的引用对象会被回收吗?原因?
不会被回收,原因是和类绑定在一起。什么时候类没有了,对应类的静态的属性也就没有了
第10章 抽象类/接口/多态
1、如何定义抽象类:
在类上加上abstract关键字 public abstract Person{}
如何定义抽象方法:
正常情况下,如果一个方法没有方法体,肯定是不行的,我们将方法定义为抽象方法,就可以没有方法体了
我们只需要在方法上加上abstract关键字,那么该方法就是抽象方法了。没有方法体就是没有后面的大括号了
public abstract void get();抽象方法只能位于抽象类中.
抽象方法和抽象类的关系: 抽象类中可以没有抽象方法 抽象方法必须位于抽象类中
当一个非抽象类继承了抽象类,那么非抽象类必须实现抽象类中的抽象方法,
当子类是抽象类,继承了抽象类,那么子类既可以实现抽象方法,也可以不实现。
抽象类可以有构造方法
2、当抽象类中的所有的方法都是抽象的,那么就可以定义成接口了
我们使用接口时,只能采用实现的方法,implements 接口名 实现接口 接口可以实现多个
当一个正常的类实现了接口,必须实现接口中所有的抽象方法
当子类是抽象类实现了接口,可以实现接口中所有的抽象方法,也可以不实现。
3、父类的引用可以指向子类的对象 多态
接口/父类 对象名(父类的引用) = new 接口的实现类(参数列表)/new 子类(参数列表)
父类的引用不能调用子类特有的属性或者方法,如果想使用子类特有的属性或者方法,可以将
父类的引用强制转换为子类的类型
第11章 内部类
匿名内部类: 多线程相关
有了匿名内部类,我们可以对抽象类和接口进行实例化了。我们可以new抽象类和接口了,
只需要实现其中的抽象方法就可以。
============================================
需要将UserDaoService 加载到内存中
UserDaoService userDaoService = new UserDaoService();
有了内部类后,我们可以直接new 接口
UserDao userDao = new UserDao(){
@Override
public void add() {
}
}
第12章 常用API
String 类
1、位于java.lang包下 用的时候我们不需要导包
2、String类代表的是字符串 ""包裹的内容属于字符串
3、字符串的不可变,一旦创建了不可更改.对原有的字符串进行任何操作,都不影响原有的对象的值,会产生一个新的对象
4、String类是否可以被继承? 不可以被继承, 因为被final修饰了 所以不能被继承
String类的构造方法形成了重载
public String() 无参构造方法 创建的字符串对象是不包含任何内容的 默认值就是: “”
public String(char value[]) 需要一个字符类型的数组 将字符类型的数组中的内容拼接成字符串
public String(String original) 需要一个字符串类型的参数 创建的字符串的内容和我传入的参数的是一致的 比较常见
String 变量名 = 值 使用直接赋值的形式创建字符串 最常见
字符串的比较:
两种情况的比较:
== 比较
双等号两边为基本数据类型,那么比较的是数据的值是否相等
双等号两边为引用数据类型,那么比较的是对象的内存地址是否相同
内容: equals 比较两个字符串的内容 区分大小写
1、问==和equasl的区别?
字符串
== 比较的是两个字符串的内存地址 equals比较的是字符串的内容
自定的类 不重写equals的前提下:
==和equals比较都是内存地址
String 使用的是一个字符数组保存字符串的内容 private final char value[]; the value is used for character storage
StringBuilder类概述 线程不安全的 效率高
是一个可变的字符串的类 我对StringBuilder对象进行任何操作,会影响到自身的值,对象的内容是可变的
String类 它的内容是不可变的
StringBuilder类 它的内容是可变的
public StringBuilder():StringBuilder对象的默认值为""
public StringBuilder(String str): StringBuilder对象的值为传入的字符串
StringBuffer类概述 线程安全的 效率低
是一个可变的字符串的类 我对StringBuilder对象进行任何操作,会影响到自身的值,对象的内容是可变的
所有的操作和StringBuilder是一模一样的
面试题:
1、直接赋值方式创建
以“”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护
1、String str = new String("abc") 创建几个对象?
2对象 一个在堆中创建 一个常量池中
2、String str = new String("abc"),String str1 = new String("abc")创建几个对象?
3个对象
3、String 是否可以被继承?原因?
4、String 存储的值是否可变?原因?
5、String str1 = "abc"; String str2 = "ab" + "c"; str1==str2是true吗?原因?
答案:是。因为String str2 = "ab" + "c"会查找常量池中时候存在内容为"abc"字符串对象,如存在则直接让str2引用该对象,显然String str1 = "abc"的时候,
上面说了,会在常量池中创建"abc"对象,所以str1引用该对象,str2也引用该对象,所以str1==str2
String str1 = "abc"; String str2 = "ab"; String str3 = str2 + "c"; str1==str3是false吗?原因?
答案:是。因为String str3 = str2 + "c"涉及到变量(不全是常量)的相加,所以会生成新的对象,
其内部实现是先new一个StringBuilder,然后 append(str2),append("c");然后让str3引用toString()返回的对象
第13章 异常
Throwable
Error: 错误 人为解决不了的 jvm层面的错误 StackOverflowError
Exception: 异常 程序在运行过程中出现的问题 称之为异常
.java文件
编译 编译时异常
.class文件
运行 运行时异常
结果
自定义异常:
编译时异常 自定义的类继承Exception 就是编译时异常/受检异常
运行时异常 自定义的类继承RuntimeException 就是运行时异常
如何解决异常:
1、try{
可能发生异常的代码
}catch(异常类型1 变量名){
//处理异常的过程
}catch(异常类型2 变量名){
//处理异常的过程
}...finally{ 可以有 可以没有
无论是否发生异常,finally中的代码都会执行
}
执行流程: 1、如果try中的代码没有发生异常,catch不执行,执行finally中的代码
2、如果try中的代码发生异常,catch捕获到异常,依次和后面的异常类型对比,
如果成功,则执行对应的异常处理代码,后面的catch都不执行了,在后finally
如果都不成功,交给jvm处理。
2、throws 放在方法的上边, 将异常抛出, 交给该方法的调用者处理。
调用该方法可能会有异常,提前先做好处理,能不能发生不一定
3、throw 满足条件是 不抛异常 不满足条件时 抛出异常
第14章 集合
14.1 单列集合Collection
14.1.1 List集合
List:集合有序(添加的顺序和集合中保存的顺序是一致的) 并且可以重复,有索引
常用的实现类:
- ArrayList 集合
原理:底层数组,查找和增加快,删除和插入慢
Object[] elementData属性数组存储集合中的元素
jdk7:在创建集合的时候 创建长度为10的数组 赋值给elementData,当添加的元素超过数组的长度时,进行扩容为原来的1.5倍.
jdk8:在创建集合的时候 创建长度为0的数组 赋值给elementData,在第一次添加元素的时,将新产生的长度为10的数组赋值给elementData,当添加的元素超过数组的长度时,进行扩容为原来的1.5倍
集合进行缩容: 不能自动发生,只能手动解决trimToSize 可以进行缩容。
构造方法:
public ArrayList(int initialCapacity) 创建指定大小空间的数组存储元素
public ArrayList()默认创建指定长度的数组
面试题:哈罗: ArrayList 这种扩容方式弊端? 堆内存溢出
在创建集合的时候,直接指定数组的长度,避免在扩容阶段新数组的产生,造成内存溢出
2、LinkedList集合
原理: 双向链表 插入和删除快 增加和查找慢
3、Vector集合
原理: 底层数组 查找和增加快 删除和插入慢
Object[] elementData 属性用来保存集合中的数据,在创建集合的时候创建长度为10的数组,并将数据赋值给elementData属性,当数组元素满的时候 会进行扩容 默认扩容为原来的2倍
面试题:
1、三者的区别:ArrayList、LinkedList、Vector
1.1、ArrayList和Vector
相同点: 底层都是数组 查找和增加数据快 删除和插入速度慢
不同点: ArrayList: 当添加的元素超过数组的长度时,进行扩容为原来的1.5倍 线程不安全的。Vector: 当数组元素满的时候 会进行扩容 默认扩容为原来的2倍 线程安全的
1.2、ArrayList和LinkedList
相同点: 单列集合
不同点: ArrayList: 底层数组: 查找和增加数据快 删除和插入速度慢,会扩容。LinkedList: 底层链表(双向链表): 删除和插入速度快,查找和增加速度慢, 不会扩容
集合遍历:
1、普通的for循环要求对应的集合必须有索引
2、增强for循环 有无索引都可以 底层还用迭代器
语法: for(集合中的数据类型 变量:集合名){
变量代表集合中元素
}
3、迭代器 有无索引都可以
1、获取到迭代器 通过集合iterator方法获取到迭代器
2、判断当前位置是否有元素 hasNext()
3、如和将当前位置的元素取出 next()
14.1.3 Set集合
Set集合:无序(不代表乱序) 不可重复, 没有索引,不能用普通的for进行遍历
常用的实现类:
1、HashSet集合
特性: 不可重复 没有索引 无序
原理: jdk7 数组+链表
jdk8 数组+链表+红黑树
源码:
1、计算传入对象的hash值
key是我们传入的对象 value就是一个常量
public V put(K key, V value) {
hash(key) 计算我们传入的对象的hash值,根据传入的对象的hashCode计算的
return putVal(hash(key), key, value, false, true);
}
2、创建长度为16的数组Node的数组并且赋值给table属性
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
// resize() 创建长度为16的数组 并且将数组赋值给table属性
// 局部变量tab和属性table指向同一块堆内存
n = (tab = resize()).length;
3、根据传入的对象的hash值和数组的长度进行相应的运算计算出传入对象的存储位置 判断计算出来的存储位置上是否有值?没有值,直接存储对应的位置上 对应元素的hash值以及本身的值
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
4、如果对应的位置上有值,比较传入的对象的hash值是否相等:
如果hash值相等,在判断他们的equals是否相等,equals也相等 添加不成功
if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
hash不相等 利用循环遍历当前位置上的链表
for (int binCount = 0; ; ++binCount) {
p.next 当前元素的下一个节点 如果为null 代表遍历到了最后一个元素说明整个链表上没有一个节点和新添加的元素是相等的
if ((e = p.next) == nul