JAVA基础
2022-02-18 11:31:23 0 举报
AI智能生成
java基础
作者其他创作
大纲/内容
三大特性
封装
定义
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。<br>数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。<br>用户无需知道对象内部的细节,但可以通过对象对外提供的接口来访问该对象<br>
优点
减少耦合
可以独立地开发、测试、优化、使用、理解和修改
有效地调节性能
可以通过剖析确定哪些模块影响了系统的性能
提高软件的可重用性
降低构建大型系统的风险
即使整个系统不可用,但是这些独立的模块却有可能是可用的
继承
继承实现了 IS-A 关系
子类拥有父类非 private 的属性、方法
继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象
父类引用指向子类对象称为 向上转型
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
子类可以用自己的方式实现父类的方法
局限性
提高了类之间的耦合性,耦合度高就会造成代码之间的联系越紧密,代码独立性越差
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性
多态
编译时多态
主要指方法的重载
在编译期可根据参数的数据类型、个数以及次序来确定调用方法
运行时多态
程序中定义的对象引用所指向的具体类型在运行期间才确定
必要条件
继承
覆盖(重写)
向上转型
作用
把不同的子类对象都当做父类来看,可以屏蔽不同子类对象之间的差异,抽象出一个通用的逻辑关系,以此来适应不同的业务需求
六大关系
泛化关系 (Generalization)
也就是继承关系,用于描述父类与子类之间的关系
父类又称作基类或超类,子类又称作派生类
在UML中,泛化关系用带空心三角形的直线来表示
实现关系 (Realization)
在这种关系中,类实现了接口,类中的操作实现了接口中所 声明的操作
在UML中,类与接口之间的实现关系用带空心三角形的虚线来表示
聚合关系 (Aggregation)
表示一个整体与部分的关系,不是强依赖
成员类是整体类的一部分,即成员对象是整体对象的一部分<br>但是成员对象可以脱离整体对象独立存在<br>
在UML中,聚合关系用带空心菱形的直线表示
组合关系 (Composition)
和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了
比如公司和部门,公司没了部门就不存在了<br>但是公司和员工就属于聚合关系了,因为公司没了员工还在<br>
在UML中,组合关系用带实心菱形的直线表示
关联关系 (Association)
表示不同类对象之间有关联,这是一种静态结构化关系,与运行过程的状态无关,在最开始就可以确定
表示一种重要的业务之间的关系,需要“持久化”的,或者说需要保存到数据库中的。<br>比如学生管理系统中的 Student 类和 Class(班级)类,一个 Student 对象属于哪个 Class 是一个重要的业务关系,如果这种关系不保存,系统就无法管<br>
是类与类之间最常用的一种关系,实现关联关系时,通常将一个类的对象作为另一个类的属性
在UML类图中,用实线连接有关联的对象所对应的类
依赖关系 (Dependency)
是一种使用关系,在运行中起作用,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系
A 类和 B 类是依赖关系主要有三种形式:
A 类是 B 类中的(某中方法的)局部变量
A 类是 B 类方法当中的一个参数
A 类向 B 类发送消息,从而影响 B 类发生变化
在UML中,依赖关系用带箭头的虚线(因为是一种弱关系)表示,由依赖的一方指向被依赖的一方
八大基本类型
基本类型
boolean
1
byte
8
char
16
short
16
int
32
float
32
long
64
double
64
缓存池
new Integer(123) 与 Integer.valueOf(123) 的区别在于
new Integer(123) 每次都会新建一个对象
Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用
valueOf() 方法的实现
public static Integer valueOf(int i) {<br> if (i >= IntegerCache.low && i <= IntegerCache.high)<br> return IntegerCache.cache[i + (-IntegerCache.low)];<br> return new Integer(i);<br>}<br>
先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容
在 Java 8 中,Integer 缓存池的大小默认为 -128~127
编译器会在缓冲池范围内的基本类型自动装箱过程调用 valueOf() 方法<br>因此多个 Integer 实例使用自动装箱来创建并且值相同,那么就会引用相同的对象<br>
Integer m = 123;<br>Integer n = 123;<br>System.out.println(m == n); // true<br>
Integer m = 323;<br>Integer n = 323;<br>System.out.println(m == n); // false<br>
基本类型对应的缓冲池
boolean values true and false <br>all byte values<br>short values between -128 and 127 <br>int values between -128 and 127 <br>char in the range \u0000 to \u007F<br>
String
定义
public final class String<br> implements java.io.Serializable, Comparable<String>, CharSequence {<br> /** The value is used for character storage. */<br> private final char value[];<br>
String 被声明为 final,因此它不可被继承<br>
内部使用 char 数组存储数据,该数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组
String 内部没有改变 value 数组的方法,因此可以保证 String 不可变
不可变的好处
可以缓存 hash 值
String 的 hash 值经常被使用,不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算
String Pool 的需要
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用
String a = "abc";<br> String b = "abc";<br> String c = new String("abc");<br> System.out.println(a == b);//true<br> System.out.println(a == c);//false
String a = "abc";内存会去查找永久代(常量池) ,如果没有的话,在永久代中中开辟一块儿内存空间,把地址付给栈指针,如果已经有了"ABC"的内存,直接把地址赋给栈指针
String c = new String("abc");是根据"abc"这个String对象再次构造一个String对象;在堆中重新new一块儿内存,把指针赋给栈,<br>将新构造出来的String对象的引用赋给c。 因此只要是new String(),则栈中的地址都是指向最新的new出来的堆中的地址
安全性
String 经常作为参数,String 不可变性可以保证参数不可变
线程安全
String 不可变性天生具备线程安全,可以在多个线程中安全地使用
Object
public final native Class<?> getClass()<br><br>public native int hashCode()<br><br>public boolean equals(Object obj)<br><br>protected native Object clone() throws CloneNotSupportedException<br><br>public String toString()<br><br>public final native void notify()<br><br>public final native void notifyAll()<br><br>public final native void wait(long timeout) throws InterruptedException<br><br>public final void wait(long timeout, int nanos) throws InterruptedException<br><br>public final void wait() throws InterruptedException<br><br>protected void finalize() throws Throwable {}<br>
equals() 与 ==
对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法
对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价
关键字
final
static
初始化顺序
父类(静态变量、静态语句块)<br>子类(静态变量、静态语句块)<br>父类(实例变量、普通语句块)<br>父类(构造函数)<br>子类(实例变量、普通语句块)<br>子类(构造函数)<br>
synchronized
volatile
反射
反射可以提供运行时的类信息
类加载
在类加载的时候,jvm会创建一个class对象
方式
根据类名:类名.class
根据对象:对象.getClass()
根据全限定类名:Class.forName(全限定类名)
Class方法
类加载相当于 Class 对象的加载<br>类在第一次使用时才动态加载到 JVM 中,可以使用 Class.forName("com.mysql.jdbc.Driver") 这种方式来控制类的加载<br>
反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;<br>每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上; <br>反射也是考虑了线程安全的,放心使用; <br>反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销; <br>反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器; <br>当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离; <br>调度反射方法,最终是由jvm执行invoke0()执行<br>
异常
Throwable 是 Java 语言中所有错误与异常的超类
Error
表示 JVM 无法处理的错误
这些错误是不受检异常,非代码性错误
比如
Virtual MachineError(虚拟机运行错误)
NoClassDefFoundError(类定义错误)
OutOfMemoryError:内存不足错误
StackOverflowError:栈溢出错误
Exception
可查的异常(checked exceptions
需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复
编译器要求必须处置的异常
不可查的异常(unchecked exceptions)
是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常
编译器不要求强制处置的异常
异常是否耗时?为什么会耗时?
建立一个异常对象,是建立一个普通Object耗时的约20倍
而抛出、接住一个异常对象,所花费时间大约是建立异常对象的4倍
泛型
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)
使用
泛型类
泛型接口
泛型方法
泛型上下限
上限(extends)
下限(super)
意义
多种数据类型执行相同的代码(代码复用)
类型擦除
背景
Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样
原则
消除类型参数声明,即删除<>及其包围的部分
根据类型参数的上下界推断并替换所有的类型参数为原生态类型:如果类型参数是无限制通配符或没有上下界限定则替换为Object,如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类)
为了保证类型安全,必要时插入强制类型转换代码
自动产生“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”
SPI(Service Provider Interface)机制
JDK内置的一种 服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用
实现
当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类
当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了
JDK中查找服务的实现的工具类是:java.util.ServiceLoader
应用
JDBC DriverManager
首先在java中定义了接口java.sql.Driver
实现
mysql实现
在mysql的jar包mysql-connector-java-6.0.6.jar中,可以找到META-INF/services目录,该目录下会有一个名字为java.sql.Driver的文件,文件内容是com.mysql.cj.jdbc.Driver
postgresql实现
同样在postgresql的jar包postgresql-42.0.0.jar中,也可以找到同样的配置文件,文件内容是org.postgresql.Driver
使用
在使用SPI扩展来加载具体的驱动,我们在Java中写连接数据库的代码的时候,不需要再使用Class.forName("com.mysql.jdbc.Driver")来加载驱动了
String url = "jdbc:xxxx://xxxx:xxxx/xxxx";<br>Connection conn = DriverManager.getConnection(url,username,password);
插件体系
eclipse的插件思想
META-INF/MANIFEST.MF: 项目基本配置信息,版本、名称、启动器等
build.properties: 项目的编译配置信息,包括,源代码路径、输出路径
plugin.xml:插件的操作配置信息,包含弹出菜单及点击菜单后对应的操作执行类等
Spring中SPI机制
在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的
从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回
其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中
缺陷
不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费
获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类
多个并发多线程使用 ServiceLoader 类的实例是不安全的
0 条评论
下一页