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

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

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

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

Java 基础我做了 10 个方面的总结,包括基本概念,面向对象,关键字,基本类型与运算,字符串与数组,异常处理,Java 平台与内存管理,分布式 Java 应用,多线程,IO。

image.png

一、基本概念

1.1 语言特点

  1. 纯面向对象
  2. 平台无关性
  3. 内置类库
  4. 支持web
  5. 安全性
    • 防止代码攻击
  6. 健壮性
    • 强类型机制
    • 垃圾回收器
    • 异常处理
    • 安全检查机制
  7. 去除C++中难以理解易混淆的特性

1.2 与 C++ 比较

  1. 解释编译混合型语言,执行速度慢,跨平台
  2. 纯面向对象,只有类,不存在全局变量或全局函数
  3. 无指针,无多继承,可多实现
  4. 垃圾回收器自动管理内存

1.3 main函数知识

  1. Java 程序入口方法
  2. 可由 finalsynchronized 修饰,不能用 abstract

1.4 Java 程序初始化顺序

  1. 静态优于非静态
  2. 父类优于子类
  3. 按照成员变量的定义顺序
  4. 总共10个:
    1. 父静态变量 -> 父静态代码块 -> 子静态变量 -> 子静态代码块 -> 父非静态代码变量 ->
    2. 父非静态代码块 -> 父构造器 -> 子非静态变量 -> 子非静态代码 -> 块子构造器

1.5 作用域与可见性

  1. 静态变量属于类
  2. 局部变量属于花括号
  3. 成员变量根据 publicprotecteddefaultprivate 可见性依次递减

image.png

1.6 构造函数

  1. 与类名相同,无返回值
  2. 可重载,不能被继承,即不能被覆盖
  3. 参数个数任意
  4. 伴随 new 一起调用,为系统调用
  5. 完成对象的初始化工作
  6. 子类可通过 super 显式调用父类。父类没有提供无参,子类必须显式调用
  7. 未定义,默认无参,修饰符取决于类修饰符

1.7 标识接口

  1. 无任何方法声明
  2. 表示实现它的类属于一个特定的类型

1.8 clone 方法

  1. 实现 Cloneable 接口
  2. 重写 Object 类中的 clone()
  3. clone() 中调用 super.clone()
  4. 把浅复制引用指向新的克隆体

1.9 反射

  1. 定义:允许程序在运行时进行自我检查,也允许对其内部成员进行操作
  2. 功能
    • 得到一个对象所属的
    • 获取一个类的所有成员和方法
    • 运行时创建对象
    • 在运行时调用对象的方法
  3. 获取类的方式
    • class.forName("类路径")
    • 类名.class
    • 实例.getClass()

1.10 创建对象的四种方式

  1. new
  2. 反射机制
  3. clone()
  4. 反序列化

1.11 package 作用

  1. 提供多层命名空间,解决命名冲突
  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.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.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不能相互转换
    • 多种类型混合运算,自动转成容量最大类型
  • 运算符优先级
    1. () []
    2. +(正) -(负) ++ -- ~ !
    3. * / %
    4. +(加) -(减)
    5. << >> >>>
    6. < <= > >= instanceof
    7. == !=
    8. &
    9. |
    10. ^
    11. &&
    12. ||
    13. ?:
    14. = += -= *= /= %= &= |= ^= ~= <<= >>= >>>=

五、字符串与数组

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.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
  • 出现异常后,一直往上层抛,直到遇到处理代码或最上层
  • 多态。若先捕获基类,再捕获子类。子类处理代码将永远不会得到执行

七、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. 标记:暂停

          1. 并发标记:恢复,轮询着色对象,以标记它们

          1. 重新标记:暂停

          1. 并发收集:恢复
        • CMS内存回收易产生碎片,但是它提供了整理碎片的功能
        • 浮动垃圾:CMS回收时产生应该回收但要等到下次CMS才能被回收掉的对象
  • Full GC
    • 对新生代旧生代及持久代都进行的GC
    • 触发的四种情况
      • 旧生代空间不足
      • 持久代空间满
      • CMS GC出现promotion failed和concurrent mode failure
      • 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间

7.9 内存泄露
  • 一个不再被程序使用的对象或变量还在内存中占有存储空间
  • 符合垃圾回收标准
    • 对象赋空值null
    • 给对象赋予新值,重新分配了内存空间
  • 泄露的两种情况
    • 堆中申请的空间没有被释放
    • 对象不再被使用,但仍然存活在内存中
  • 泄露原因
    • 静态集合类
    • 各种连接
    • 监听器
    • 变量不合理的作用域
    • 单例模式

八、分布式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文件

        1. 将应用+WSDL文件放入HTTP服务器

        1. 借用Java辅助工具根据WSDL文件生成客户端stub代码

        1. stub将产生的对象请求信息封装为标准化的SOAP格式数据,并发请求到服务器端

        1. 服端在接收到SOAP格式数据时进行转化,反射调用相应的Java类
      • SOAP优点支持跨语言,缺点对复杂对象结构难支持

8.3 基于开源框架
  • Spring RMI

九、多线程

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方法后面的代码
  • 线程合并,实现同步功能

十、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接口控制

image.png