前言

大学期间接触 Java 的时间也不短了,不论学习还是实习,都让我发觉基础的重要性。互联网发展太快了,各种框架各种技术更新迭代的速度非常快,可能你刚好掌握了一门技术的应用,它却已经走在淘汰的边缘了。

而学习新技术总要付出一定的时间成本,那么怎么降低时间成本呢?那就是打好基础,技术再怎么革新,底层的东西也很少会变动,牢固的基础会帮助你在各种新技术的学习中游刃有余,快速上手。

因为我选择的方向是后台开发,所以谈谈我认为的基础有哪些。其他方向肯定也有自己的体系,从低层到高层,可以自己摸索。后台的话,我觉得网络知识,各种协议,web 知识,数据库知识,Linux 基本操作以及自己选择的后台语言知识,这些是最基础最需要牢固掌握的。

所以从今天起,会出一系列与后台基础相关的博文,一是对自己过去学习的一个总结,二是分享出来,希望可以帮助到需要的人。

概要

Java 基础我做了 10 个方面的总结,包括基本概念,面向对象,关键字,基本类型与运算,字符串与数组,异常处理,Java 平台与内存管理,分布式 Java 应用,多线程,IO。以下对这些内容做一些简单的总结,同时我也有完整的思维导图,博客上不方便展示

细节

1. 基本概念

1.1 语言特点
  • 纯面向对象
  • 平台无关性
  • 内置类库
  • 支持web
  • 安全性
  • 防止代码攻击
  • 健壮性
  • 强类型机制
  • 垃圾回收器
  • 异常处理
  • 安全检查机制
  • 去除C++中难以理解易混淆的特性
1.2 与C++比较
  • 解释编译混合型语言,执行速度慢,跨平台
  • 纯面向对象,只有类,不存在全局变量或全局函数
  • 无指针,无多继承,可多实现
  • 垃圾回收器自动管理内存
1.3 main函数知识
  • Java程序入口方法
  • 可由final,synchronized修饰,不能用abstract
1.4 Java程序初始化顺序
  • 静态优于非静态
  • 父类优于子类
  • 按照成员变量的定义顺序
  • 总共10个
1.5 作用域与可见性
  • 静态变量属于类
  • 局部变量属于花括号
  • 成员变量看下一条
  • public、protected、default、private 可见性依次递减
1.6 构造函数
  • 与类名相同,无返回值
  • 可重载,不能被继承,即不能被覆盖
  • 参数个数任意
  • 伴随new 一起调用,为系统调用
  • 完成对象的初始化工作
  • 子类可通过super显式调用父类。父类没有提供无参,子类必须显式调用
  • 未定义,默认无参,修饰符取决于类修饰符
1.7 标识接口
  • 无任何方法声明
  • 表示实现它的类属于一个特定的类型
1.8 clone 方法
  • 实现Cloneable接口
  • 重写Object类中的clone()
  • clone()中调用super.clone()
  • 把浅复制引用指向新的克隆体
1.9 反射
  • 定义:允许程序在运行时进行自我检查,也允许对其内部成员进行操作
  • 功能
  • 得到一个对象所属的类
  • 获取一个类的所有成员和方法
  • 运行时创建对象
  • 在运行时调用对象的方法
  • 获取类的方式
  • class.forName("类路径")
  • 类名.class
  • 实例.getClass()
1.10 创建对象的四种方式
  • new
  • 反射机制
  • clone()
  • 反序列化
1.11 package 作用
  • 提供多层命名空间,解决命名冲突
  • 对类按功能进行分类,使项目组织更加清晰

2. 面向对象

2.1 与面向过程区别
  • 层次逻辑关系不同。
  • 面向对象是通过类的层次结构来体现类之间的继承与发展
  • 面向过程是通过模块的层次结构概括模块与模块间的关系与功能
  • 数据处理方式不同与控制程序方式不同
  • 面向对象是数据与操作封装成一个整体,通过事件驱动来激活和运行程序
  • 面向过程是数据单独存储,控制程序方式上按照设计调用或返回程序
2.2 特性
  • 抽象
  • 继承
  • 多态
  • 封装
2.3 这种开发方式优点
  • 开发效率高。代码重用
  • 保证软件的鲁棒性。经过长期测试的已有代码
  • 保证软件的高可维护性。设计模式成熟
2.4 继承
  • 单继承
  • 只能继承父类的非私有成员变量和方法
  • 同名成员变量,子类覆盖,不会继承
  • 相同函数签名,子类覆盖,不会继承
2.5 组合和继承区别
  • 组合:在新类中创建原有类的对象。has a
  • 继承是 is a
2.6 多态
  • 方法重载
  • 编译时多态
  • 方法覆盖
  • 运行时多态
  • 成员变量无多态概念
2.7 覆盖和重载区别
  • 子父类关系,垂直;同类方法间关系,水平
  • 一对方法发生关系;多个方法发生关系
  • 参数列表相同;参数列表不同
  • 调用的方法根据对象的类型决定;根据调用时的实参表决定方法体
2.8 抽象类与接口异同

  • 不能被实例化
  • 接口的实现类实现了接口,抽象类的子类实现了方法,才能被实例化

  • 接口只能定义方法,不能实现;抽象类可以有定义和实现
  • 接口需要被实现;抽象类需要被继承
  • 接口强调特定功能的实现;抽象类强调所属关系
  • 接口成员变量默认为 public static final,成员方法 public abstract
  • 抽象类变量默认default,方法不能用 private、static、synchronized、native 修饰
2.9 内部类
  • 静态内部类
  • static 修饰
  • 只能访问外部类中的static数据
  • 成员内部类
  • 与实例绑定
  • 不可定义静态属性和方法
  • 外部实例化后,该内部类才能被实例化
  • 局部内部类
  • 代码块内
  • 不能被public、protected、private以及static修饰
  • 只能访问final 局部变量
  • 匿名内部类
  • 无类名
  • 无构造函数,必须继承或实现其他类
  • 原则
  • 无构造函数
  • 无静态成员,方法和类
  • 不能是public、protected、private、static
  • 只能创建匿名内部类的一个实例
  • new 后面有继承或实现
  • 特殊的局部内部类
2.10 如何获取父类类名
  • 利用反射:obj.getClass().getSuperClass().getName()
  • 不使用super.getClass()原因:该方法在 Object中为final与native,子类不能覆盖,返回此Object运行时类
2.11 this
  • 指向当前实例对象
  • 区分成员变量与方法形参
2.12 super
  • 访问父类成员变量或方法
  • 子类同名会覆盖,访问父类只能通过super
  • 子类构造函数需显示调用父类构造函数时,super()必须为构造函数的第一条语句

3. 关键字

3.1 变量命名
  • 英文字母
  • 数字
  • _和$
  • 不能包含空白字符
  • 首字符不能为数字
  • 保留字不能做标识符
  • 区分大小写
3.2 assert
  • 软件调试
  • 运行时开启 -ea
3.3 static
  • 特定类的统一存储空间,类绑定
  • 成员变量:属于类,内存中只有一个复制
  • 成员方法:调静态数据。可实现单例模式
  • 代码块:初始化静态变量,只被执行一次
  • 内部类:不能与外部类重名,只能访问外部类静态数据(包括私有)
3.4 switch
  • 多分支选择
  • 整型或字符类型变量或整数表达式
  • Java 7 开始支持 String。原理是String的hashCode()返回的int类型值匹配
3.5 volatile
  • 保证线程间的可见性
  • 从内存中取数据,而不是缓存
  • 不保证原子性
3.6 instanceof
  • 二元运算符
  • 判断一个引用类型的变量所指向的对象是否是一个类的实例
  • 即左边对象是否是右边类的实例
3.7 strictfp
  • 精确浮点
  • 确保浮点运算的准确性
  • 若不指定,结果依赖于虚拟机平台
  • 指定后依赖于统一标准,保证各平台的一致性
3.8 null
  • 不是合法的Object实例
  • 无内存
  • 表明该引用目前没有指向任何对象

4. 基本类型与运算

4.1 基本数据类型
  • int长度
  • byte(8 bit)
  • short(16 bit)
  • int(32 bit)
  • long(64 bit)
  • float长度
  • 单精度(32 bit float)
  • 双精度(64 bit double)
  • boolean 类型变量的取值
  • true
  • false
  • char数据类型:Unicode字符(16 bit)
  • void:java.lang.Void 无法直接对其进行操作
4.2 不可变类
  • 实例创建后,值不可变
  • 所有的基本类型的包装类+String
  • 优点
  • 使用简单
  • 线程安全
  • 节省内存
  • 缺点:会因为值的不同而产生新的对象,导致无法预料的问题
4.3 类型转换
  • 隐式类型转换
  • 低精度到高精度
  • byte->short->char->int->long->float->double
  • 显式类型转换
  • 反之
  • 可能会损失精度
  • 类型自动转换
  • 低到高
  • char类型会转换为其对应的ASCII码
  • byte、char、short参与运算自动转为int,但"+=",不转
  • 基本数据类型与boolean不能相互转换
  • 多种类型混合运算,自动转成容量最大类型
  • 运算符优先级
点	()	[]
  +(正)	-(负)		++	--	~	!
  *	/	%
  +(加)	-(减)
  <<	>>	>>>
  <	<=	>	>=	instanceof
  ==	!=
  &
  |
  ^
  &&
  ||
  ?:
  =	+=	-=	*=	/=	%=	&=       |=	^=	~=	<<=	>>=	>>>=

5. 字符串与数组

5.1 字符串创建与存储机制
  • 常量池
  • new String("abc")创建1个或2个对象
5.2 ==、equals和hashCode区别
  • == 比较引用,内存
  • 未覆盖,同==;比较内容
  • hashCode鉴定对象是否相等,返回整数
5.3 String,StringBuffer,StringBuilder
  • String:不可变,执行效率最低
  • StringBuffer:可修改,线程安全,效率较高
  • StringBuilder:可修改,线程不安全,效率最高
5.4 其他
  • 数组初始化方式
  • length属性和length()方法

6. 异常处理

6.1 finally块执行时机
  • 若try中有return,在return前
  • 若try-finally或catch-finally中都有return,finally会覆盖
6.2 finally代码块不是一定会被执行
  • 程序进入try之前出现异常
  • try中调用System.exit(0)
6.3 Error

严重错误,不可恢复

6.4 Exception
  • 可恢复,编译器可捕捉
  • 检查性异常
  • IO
  • SQL
  • 运行时异常
  • JVM处理
  • NullPointException
  • ClassCastException
  • ArrayIndexOutOfBoundsException
  • 出现异常后,一直往上层抛,直到遇到处理代码或最上层
  • 多态。若先捕获基类,再捕获子类。子类处理代码将永远不会得到执行

7. Java平台与内存管理

7.1 Java平台与其他语言平台的区别
  • 纯软件,包括JVM与JAVA API
  • JVM虚拟,不跨平台
7.2 JAVA代码的执行
  • 代码编译为class:sun jdk 中javac
  • 装载class:ClassLoader
  • 执行class
  • 解释执行
  • 编译执行
  • client compiler
  • server compiler
7.3 java源码编译机制
  • 词法分析器组件:Token流
  • 语法分析器组件:语法树
  • 语义分析器组件:注解语法树
  • 将语法树中的名字、表达式等元素与变量、方法、类型等联系到一起
  • 检查变量使用前是否已声明
  • 推导泛型方法的类型参数
  • 检查类型匹配性
  • 进行常量折叠
  • 检查所有语句都可到达
  • 检查变量的确定性赋值
  • 解除语法糖
  • 将泛型JAVA转成普通Java
  • 检查所有checked exception都被捕获或抛出
  • 将含语法糖的语法树转成简单语法树eg:foreach,自动折叠
  • 代码生成器组件:字节码
7.4 类加载机制
  • 装载:全限定名+类加载器加载类
  • 链接
  • 校验
  • 格式不符,抛VerifyError
  • 加载引用的类失败:抛NoClassDefFoundError
  • 准备:静态变量默认初始化
  • 解析:属性、方法验证(可选)
  • 初始化(不是类加载必须触发的)
  • 静态初始化代码
  • 构造器代码
  • 静态属性初始化
  • 触发时机
  • 调用了new
  • 反射调用了类中的方法
  • 子类调用了初始化
  • JVM启动过程中指定的初始化类
  • Bootstrap Class Loader:$JAVA_HOME/jre/lib/rt.jar
  • Extension Class Loader:$JAVA_HOME/jre/lib/ext/*.jar
  • System Class Loader:$CLASSPATH
  • User Defined Class Loader
7.5 类执行机制
  • 解释执行
  • JVM字节码为中间代码,由JVM在运行期对其解释并执行
  • invokestatic
  • invokevirtual
  • invokeinterface
  • invokespecial
  • 基于栈
  • 代码紧凑,体积小
  • 线程创建后,产生PC和Stack
  • 指令解释执行
  • 栈顶缓存:栈顶值缓存在寄存器上
  • 部分栈帧共享
  • 编译执行
  • client compiler
  • 轻量级,占内存少
  • 方法内联
  • 去虚拟化
  • 冗余消除
  • server compiler
  • 重量级,占内存多
  • 逃逸分析是C2进行很多优化的基础
  • 标量替换:用标量替换聚合量
  • 栈上分配
  • 若对象未逃逸,C2会选择在栈上直接创建Point对象实例,而不是在堆上
  • 栈上分配更快速,对象易回收
  • 同步消除:如果发现同步的对象未逃逸,那也没有同步的必要。C2会去掉同步代码块
7.6 内存空间
  • 方法区:类信息,线程共享
  • 对象实例+数组
  • 分代管理
  • 新生代
  • 旧生代
  • 本地方法栈:支持native方法,Sun JDK的实现中本地方法栈和JVM方法栈是同一个
  • PC寄存器:线程私有
  • JVM方法栈:线程私有
7.7 内存分配
  • Java对象,堆上分配,分配需加锁,开销大
  • 当堆上空间不足-->GC-->仍不足-->抛OutOfMemory
  • Sun JDK 为新创建的线程在Eden上分配TLAB
  • 多个小对象比大对象分配更高效
  • 基于逃逸分析直接从栈上分配
7.8 内存回收
  • 收集器
  • 引用计数收集器
  • 计数器增减有消耗
  • 不适合循环引用
  • 跟踪收集器
  • 集中式管理
  • 全局记录数据的引用状态
  • 从根集合扫描对象,可能会造成应用程序暂停
  • 三种实现算法
  • 复制
  • 适用于回收空间中存活对象较少
  • 缺点:需要增加一块空的内存空间及进行对象的移动
  • 标记-清除:会产生内存碎片
  • 标记-压缩:不产生内存碎片
  • Sun JDK中可用GC
  • 新生代
  • 串行GC(Serial GC):复制算法
  • Minor GC
  • 强软弱虚
  • 并行回收GC(Parrallel Scavenge):扫描复制多线程
  • 并行 GC(ParNew):配合旧生代 CMS
  • 旧生代和持久代可用GC
  • 串行:标记压缩+清除
  • 并行:标记压缩
  • 并发:CMS
  1. 标记:暂停
  2. 并发标记:恢复,轮询着色对象,以标记它们
  3. 重新标记:暂停
  4. 并发收集:恢复
  • CMS内存回收易产生碎片,但是它提供了整理碎片的功能
  • 浮动垃圾:CMS回收时产生应该回收但要等到下次CMS才能被回收掉的对象
  • Full GC
  • 对新生代旧生代及持久代都进行的GC
  • 触发的四种情况
  • 旧生代空间不足
  • 持久代空间满
  • CMS GC出现promotion failed和concurrent mode failure
  • 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间
7.9 内存泄露
  • 一个不再被程序使用的对象或变量还在内存中占有存储空间
  • 符合垃圾回收标准
  • 对象赋空值null
  • 给对象赋予新值,重新分配了内存空间
  • 泄露的两种情况
  • 堆中申请的空间没有被释放
  • 对象不再被使用,但仍然存活在内存中
  • 泄露原因
  • 静态集合类
  • 各种连接
  • 监听器
  • 变量不合理的作用域
  • 单例模式

8. 分布式Java应用

8.1 基于消息方式实现系统间的通信
  • TCP/IP+BIO
  • socket.setSoTimeOut()设置等待响应的超时时间
  • 一连接一线程
  • 缺点:无论连接是否真实,都要创建线程
  • BIO下服务器端所能支撑的连接数目有限
  • TCP/IP+NIO
  • Channel
  • SocketChannel:建立连接,监听事件,操作读写
  • ServerSocketChannel:监听端口,监听连接事件
  • Selector:获取是否要处理的事件
  • Buffer:存放处理的数据
  • NIO Reactor模式,通过注册感兴趣的事件及扫描是否有感兴趣的事件发生,从而做出相应的动作
  • 多个请求,连接复用
  • 只有在有真实的请求时,才会创建线程
  • 一请求一线程
  • UDP/IP+BIO
  • DatagramSocket:负责监听端口,读写数据
  • DatagramPacket:作为数据流对象进行传输
  • UDP/IP+NIO
  • DatagramChannel:监听端口,进行读写
  • ByteBuffer:数据流传输
  • NIO好处:只在有流要读取或可写入流时才做出相应的IO操作,而不像BIO方式阻塞当前线程
8.2 基于远程调用方式实现系统间的通信
  • 远程调用方式
  • 系统间通信和系统内一样
  • 让使用者感觉调用远程同调用本地一样
  • 基于Java自身技术
  • RMI:客户端代理,stub,封装对象,序列化为流,TCP/IP BIO,Skeleton,反序列化,获取对象实例,调用
  • WebService
  1. 服务端的服务生成WSDL文件
  2. 将应用+WSDL文件放入HTTP服务器
  3. 借用Java辅助工具根据WSDL文件生成客户端stub代码
  4. stub将产生的对象请求信息封装为标准化的SOAP格式数据,并发请求到服务器端
  5. 服端在接收到SOAP格式数据时进行转化,反射调用相应的Java类
  • SOAP优点支持跨语言,缺点对复杂对象结构难支持
8.3 基于开源框架
  • Spring RMI

9. 多线程

9.1 线程资源同步机制
  • JVM保证以下操作顺序
  • 同一线程操作
  • 对于main Memory 上的同一个变量的操作
  • 对于加了锁的main Memory上的对象操作
  • 为避免资源操作的脏数据问题,JVM提供了
  • synchronized
  • volatile
  • lock/unlock
  • 目的是控制资源竞争
9.2 线程交互机制
  • 基于Object的wait/notify/notifyAll
  • 为避免假唤醒,需要double check
  • 调用对象的wait-->wait sets--->释放锁--->其他线程notify---->wait sets---->执行此对象线程--->删除sets中此线程
  • 基于JDK 5 并发包,支持线程交互
  • Semphore的acquire,release
  • Condition的await,signal
  • CountDownLatch的await和countDown
9.3 线程状态
  • New
  • Runnable
  • Running
  • Wait
  • TimedWait
  • Blocked
  • Terminated
9.4 sleep()与wait()方法的区别
  • sleep
  • 暂停一段时间执行
  • Thread的静态方法
  • 不释放锁
  • 需要捕获异常
  • wait
  • 使线程暂停执行
  • Object方法,用于线程间通信
  • 释放锁
9.5 守护线程
  • 后台提供服务
  • 用户线程全部终止,只剩下守护线程时,JVM就会退出
  • 调用start()之前,调用线程对象的setDaemon(true)
9.6 join
  • 调用该方法的线程在执行完run()后,再执行join方法后面的代码
  • 线程合并,实现同步功能

10. IO

10.1 流本质
  • 数据传输
10.2 流分类
  • 字节流:不使用缓存
  • 字符流
  • 码表映射
  • 使用缓存
10.3 装饰者模式
  • 运行时动态给对象增加额外的职责
  • 是你还有你,一切拜托你
  • FilterInputStream
10.4 Java Socket
  • ServerSocket server = new ServerSocket(2000);
  • Socker socket = server.accept();
  • 客户端:Socket socket = new Socket("localhost",2000);
10.5 NIO
  • Channel--Selector--Buffer
  • 反应器模式
10.6 序列化
  • 对象持久化方式
  • 解决在对对象流进行读写操作时引发的问题
  • 对象写进流里进行网络传输,保存到文件,数据库
10.7 如何实现序列化
  • 实现Serializable接口
  • 使用FileOutputStream来构造ObjectOutputStream对象
  • 使用该对象的writeObject(obj)方法将对象写出
  • 要恢复时,使用对应的输入流
10.8 序列化特点
  • 一个类能被序列化,它的子类也能被序列化
  • static代表类成员,transient代表临时数据。均不能被序列化
  • 序列化影响性能,需要才使用
  • 需要通过网络来发送对象,或对象的状态需要被持久化到数据库或文件中
  • 序列化能实现深复制,即可以复制引用的对象
10.9 反序列化
  • 将流转化为对象
  • UID最好自己定义。优点
  • 提高程序运行效率。省去计算过程
  • 提高程序不同平台兼容性。不同计算方式,反序列化失败
  • 增强程序各个版本的可兼容性。加入新属性,默认UID变化
10.10 外部序列化
  • 实现Externalizable接口控制