导图社区 java基础脑图
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念。下图梳理了基础程序设计、面向对象编程、应用程序开发,值得收藏学习哦!
编辑于2021-08-10 14:41:30java基础
基础程序设计
关键字
标识符
数据类型
基本数据类型
数值型
整数类型
byte(1字节=8bit):范围为-128~127
short(2字节)
int(4字节)
long(8字节):声明long型变量,必须以"l"或"L"结尾
浮点型
float:定义float类型变量时,变量要以"f"或"F"结尾
double
字符型:char (1字符=2字节)
布尔型:boolean
自动类型转换
结论:当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型。
byte 、char 、short --> int --> long --> float --> double
特别的:当byte、char、short三种类型的变量做运算时,结果为int型
引用数据类型
类(class)
接口(interface)
数组([ ])
运算符
算术运算符: + - + - * / % (前)++ (后)++ (前)-- (后)--
(前)++ :先自增1,后运算
(后)++ :先运算,后自增1
赋值运算符:= += -= *= /= %=
比较运算符(关系运算符): == != > < >= <=
逻辑运算符:& && | || ! ^
位运算符:<< >> >>> & | ^ ~
<< :在一定范围内,每向左移1位,相当于 * 2 >> :在一定范围内,每向右移1位,相当于 / 2
位运算符操作的都是整型的数据
三元运算符:(条件表达式)? 表达式1 : 表达式2
流程控制
分支结构
if-else条件判断结构
switch-case选择结构
循环结构
for循环结构
while循环结构
break和continue关键字的使用
数组
排序算法
手写冒泡排序
数据结构
一维数组
二维数组
Arrays工具类
面向对象编程
类与对象
创建对象内存解析
匿名对象
“万事万物皆对象”
类的结构
属性
声明在类中
内存中加载到堆空间(非static)
属性赋值的先后顺序
① 默认初始化② 显式初始化③ 构造器中初始化④ 通过"对象.方法" 或 "对象.属性"的方式,赋值
方法
声明:权限修饰符 返回值类型 方法名(形参列表){方法体 }
return关键字:结束方法,返回数据
方法的重载:"两同一不同":同一个类、相同方法名 参数列表不同:参数个数不同,参数类型不同
可变个数形参的方法
格式:数据类型 ... 变量名
与本类中方法名相同,形参不同的方法之间构成重载
与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
可变个数形参在方法的形参中,最多只能声明一个可变形参
java的值传递机制
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
递归方法
自己调用自己,隐式循环
向已知方向递归,防止死循环
方法的重写
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作.
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
构造器
作用
创建对象
初始化对象的信息
使用说明
如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
定义构造器的格式:权限修饰符 类名(形参列表){}
一个类中定义的多个构造器,彼此构成重载
一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
一个类中,至少会有一个构造器
JavaBean
类是公共的
一个无参的公共的构造器
属性,且对应的get、set方法
代码块
作用:用来初始化类、对象的信息
代码块要是使用修饰符,只能使用static
静态代码块
随着类的加载而执行,而且只执行一次
作用:初始化类的信息
静态代码块的执行要优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
非静态代码块
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块
作用:可以在创建对象时,对对象的属性等进行初始化
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:由父及子,静态先行。
内部类
定义
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类.
内部类的分类
成员内部类(静态、非静态 )
一方面,作为外部类的成员: >调用外部类的结构 >可以被static修饰 >可以被4种不同的权限修饰
另一方面,作为一个类: > 类内可以定义属性、方法、构造器等 > 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承 > 可以被abstract修饰
创建成员内部类的对象
静态的成员内部类
Person.Dog dog = new Person.Dog();
非静态的成员内部类
Person p = new Person();Person.Bird bird = p.new Bird();
成员内部类中调用外部类的结构
System.out.println(this.name);//内部类的属性System.out.println(Person.this.name);//外部类的属性
局部内部类(方法内、代码块内、构造器内)
总结:成员内部类和局部内部类,在编译以后,都会生成字节码文件。格式:成员内部类:外部类$内部类名.class 局部内部类:外部类$数字 内部类名.class
三大特性
封装性
高内聚、低耦合
权限修饰符
private
同类
缺省
同包
protected
不同包的子类
public
同工程
修饰类的话,只能使用:缺省、public
继承性
好处:① 减少了代码的冗余,提高了代码的复用性② 便于功能的扩展 ③ 为之后多态性的使用,提供了前提
class A extends B{}
一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已。
Java中类的单继承性:一个类只能有一个父类
子类对象实例化全过程
从结果上看:继承性> 子类继承父类以后,就获取了父类中声明的属性或方法。> 创建子类的对象,在堆空间中,就会加载所父类中声明的属性。
从过程上看:当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,...直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用。
强调说明:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
多态性
何为多态性:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
多态性的使用:虚拟方法调用> 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。> 总结:编译,看左边;运行,看右边。
多态性的使用前提:① 类的继承关系 ② 方法的重写
注意点:对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
向上转型与向下转型:
向上转型:多态
向下转型:
使用强制类型转换符:()
使用强转时,可能出现ClassCastException的异常。为了避免异常先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
instanceof的使用
a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。
要求a所属的类与类A必须是子类和父类的关系,否则编译错误。
Object类的使用
说明
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
Object类只声明了一个空参的构造器
equals()方法
Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
重写的原则:比较两个对象的实体内容是否相同.
toString()方法
当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
像String、Date、File、包装类等都重写了Object类中的toString()方法。 使得在调用对象的toString()时,返回"实体内容"信息
自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
包装类的使用
为了使基本数据类型的变量具有类的特征,引入包装类
基本数据类型与对应的包装类:
byte(Byte)、short(Short)、int(Integer)、long(Long)、float(Float)、double(Doube)、boolean(Boolean)、char(Character)
需要掌握的类型间的转换:(基本数据类型、包装类、String)
需要掌握的类型间的转换:(基本数据类型、包装类、String)
基本数据类型、包装类--->String:调用String重载的valueOf(Xxx xxx)
String--->基本数据类型、包装类:调用包装类的parseXxx(String s) 注意:转换时,可能会报NumberFormatException
关键字
this
this调用属性、方法:
this理解为:当前对象 或 当前正在创建的对象
方法或者构造器的形参跟类的属性同名时必须显示:this.变量
this调用构造器
在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
构造器中不能通过"this(形参列表)"方式调用自己
规定:"this(形参列表)"必须声明在当前构造器的首行
package/import
使用package声明类或接口所属的包,声明在源文件的首行
import:导入
可以使用"xxx.*"的方式,表示可以导入xxx包下的所结构
import static:导入指定类或接口中的静态结构:属性或方法。
super
可以理解为:父类的
super调用属性、方法:
通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或法。但是,通常情况下,我们习惯省略"super."
当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
super调用构造器:
在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器(必须首行)
在构造器的首行,没显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器:super()
static
可以用来修饰的结构:主要用来修饰类的内部结构:属性、方法、代码块、内部类
static修饰属性:静态变量(或类变量)
属性,是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量) 实例变量:创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。 静态变量:创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
static修饰属性的其他说明: ① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用 ② 静态变量的加载要早于对象的创建。 ③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
static修饰方法:静态方法、类方法
① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
静态方法 非静态方法 类 yes no 对象 yes yes
③ 静态方法中,只能调用静态的方法或属性 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
static的注意点
在静态的方法内,不能使用this关键字、super关键字
关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。
单例模式
类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
饿汉式
懒汉式
final
final 修饰类
此类不能被其他类所继承。比如:String类、System类、StringBuffer类
final 修饰方法
表明此方法不可以被重写比如:Object类中getClass();
final 修饰变量
此时的"变量"就称为是一个常量
tatic final 用来修饰属性:全局常量
abstract
修饰类:抽象类
此类不能实例化
抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 --->抽象的使用前提:继承性
修饰方法:抽象方法
抽象方法只有方法的声明,没方法体
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
若子类重写了父类中的所的抽象方法后,此子类方可实例化
若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
注意点
abstract不能用来修饰:属性、构造器等结构
abstract不能用来修饰私方法、静态方法、final的方法、final的类
模板方法的设计模式
在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
接口
说明
接口使用interface来定义
如何定义接口:定义接口中的成员
JDK7及以前
只能定义全局常量和抽象方法>全局常量:public static final的.但是书写时,可以省略不写>抽象方法:public abstract的
JDK8
除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
接口中定义的静态方法,只能通过接口来调用。
JDK9
方法的访问权限修饰符可以声明为private的,此时方法将不会成为你对外暴露的API的一部分。
接口中不能定义构造器的!意味着接口不可以实例化
Java开发中,接口通过让类去实现(implements)的方式来使用. 如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化 如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类
Java类可以实现多个接口 --->弥补了Java单继承性的局限性 格式:class AA extends BB implements CC,DD,EE
如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没重写此方法的情况下,报错。-->接口冲突。这就需要我们必须在实现类中重写此方法
接口与接口之间可以继承,而且可以多继承
接口的具体使用,体现多态性
接口,实际上可以看做是一种规范
工厂的设计模式
实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
应用程序开发
集合
多线程
程序、进程、线程的理解
程序(programm)
是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码。
进程(process)
程序的一次执行过程,或是正在运行的一个程序。
说明:进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread)
进程可进一步细化为线程,是一个程序内部的一条执行路径。说明:线程作为调度和执行的单位,每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。每个线程,拥有自己独立的:栈、程序计数器多个线程,共享同一个进程中的结构:方法区、堆。
并行与并发
单核CPU与多核CPU的理解
好比高速收费站单核:一个工作人员多核:多个工作人员
一个Java应用程序java.exe,其实至少三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
并行与并发的理解
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事
创建多线程的两种方式
方式一:继承Thread类的方式:
1. 创建一个继承于Thread类的子类2. 重写Thread类的run() --> 将此线程执行的操作声明在run()中3. 创建Thread类的子类的对象4. 通过此对象调用start():①启动当前线程 ② 调用当前线程的run()
说明两个问题:问题一:我们启动一个线程,必须调用start(),不能调用run()的方式启动线程。问题二:如果再启动一个线程,必须重新创建一个Thread子类的对象,调用此对象的start().
方式二:实现Runnable接口的方式
1. 创建一个实现了Runnable接口的类2. 实现类去实现Runnable中的抽象方法:run()3. 创建实现类的对象4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象5. 通过Thread类的对象调用start()
两种方式的对比:
开发中:优先选择:实现Runnable接口的方式原因:1. 实现的方式没类的单继承性的局限性 2. 实现的方式更适合来处理多个线程共享数据的情况。联系:public class Thread implements Runnable相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。目前两种方式,要想启动线程,都是调用的Thread类中的start()
Thread类中的常用的方法
start():启动当前线程;调用当前线程的run()
run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
currentThread():静态方法,返回执行当前代码的线程
getName():获取当前线程的名字;setName():设置当前线程的名字
yield():释放当前cpu的执行权
join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。
isAlive():判断当前线程是否存活
线程的优先级
MAX_PRIORITY:10MIN _PRIORITY:1NORM_PRIORITY:5 -->默认优先级
如何获取和设置当前线程的优先级:getPriority():获取线程的优先级setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只当高优先级的线程执行完以后,低优先级的线程才执行。
Thread的生命周期
线程的同步机制
在Java中,我们通过同步机制,来解决线程的安全问题。(多个线程操作同一份数据)
方式一:同步代码块
synchronized(同步监视器){ //需要被同步的代码 }
说明:1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。 2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。 要求:多个线程必须要共用同一把锁。
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。 在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
关于同步方法的总结: 1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。 2. 非静态的同步方法,同步监视器是:this 静态的同步方法,同步监视器是:当前类本身
方式三:Lock锁 --- JDK5.0新增
面试题:synchronized 与 Lock的异同? 相同:二者都可以解决线程安全问题 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器 Lock需要手动的启动同步(lock(),同时结束同步也需要手动的实现(unlock())
使用的优先顺序: Lock ---> 同步代码块(已经进入了方法体,分配了相应资源 ) ---> 同步方法(在方法体之外)
利弊
同步的方式,解决了线程的安全问题。---好处操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。
线程安全的单例模式(懒汉式)
死锁问题
IO流
多线程
反射
异常处理
异常体系
体系结构
java.lang.Throwable
java.lang.Error:一般不编写针对性的代码进行处理。
java.lang.Exception:可以进行异常的处理
编译时异常(checked)
IOException
FileNotFoundException
ClassNotFoundException
运行时异常(unchecked,RuntimeException)
NullPointerException
ArrayIndexOutOfBoundsException
ClassCastException
NumberFormatException
InputMismatchException
ArithmeticException
从程序执行过程
编译时异常
编译时异常:执行javac.exe命名时,可能出现的异常
运行时异常
运行时异常:执行java.exe命名时,出现的异常
异常的处理
java异常处理的抓抛模型
过程一:"抛":程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行。 关于异常对象的产生:① 系统自动生成的异常对象 ② 手动的生成一个异常对象,并抛出(throw)
过程二:"抓":可以理解为异常的处理方式:① try-catch-finally ② throws
异常处理方式一:try-catch-finally
finally是可选的。
使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码
catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()
在try结构中声明的变量,再出了try结构以后,就不能再被调用
try-catch-finally结构可以嵌套
finally的再说明
finally是可选的
finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中return语句,catch中return语句等情况
像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
异常处理方式二:"throws + 异常类型"
写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!
对比两种处理方式
try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。
总结:如何看待代码中的编译时异常和运行时异常?
体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。
手动抛出异常对象
使用说明在程序执行中,除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。
例子:throw new RuntimeException("您输入的数据非法!");
自定义异常类
[面试题] throw 和 throws区别:throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。throws 属于异常处理的一种方式,声明在方法的声明处。
如何自定义异常类?
1. 继承于现的异常结构:RuntimeException 、Exception
2. 提供全局常量:serialVersionUID
3. 提供重载的构造器
网络编程
java新特性