文章目录
Java全栈
注释
文档注释
@author—作者
@param—参数
@return—返回值
@throw—可能产生的异常
特性
- 封装
隐藏细节,提供接口。
- 继承
继承实现了Is A关系,比如Cat和Animal,Cat继承自Animal,从而获得非private属性和方法。
继承应遵循里氏替换原则子类对象必须替换掉父类的所有对象。
父类引用子类对象称为向上转型
- 多态
分为编译时多态和运行时多态。
-
编译时多态主要指方法的重载
-
运行时多态是指程序中定义的对象引用所指的具体类型在具体运行期间才确定。
运行时多态的三个条件:
- 继承
- 覆盖(重写)
- 向上转型
类图
- 泛化关系(Generalization)
用来描述继承关系,使用extends
- 实现关系(Realization)
用来实现一个接口,使用implement
- 聚合关系(Aggregation)
表示整体由部分构成,但是整体和部分不是强依赖关系,整体不存在了部分还是会存在。
- 组合关系(Composition)
与聚合关系不同,整体和部分之间是强依赖关系,整体部分不存在部分也就随之不存在了。
- 关联关系(Association)
表示不同对象之间有关联,这种关联是静态的,在运行之前就已经是已知的了。
- 依赖关系(Dependency)
与关联关系不同的是,依赖关系是在运行过程中起作用的。
主要表现为三种形式:
- A类是B类中(方法中)的局部变量。
- A类是B类方法中的一个参数。
- A类向B类发送消息,从而影响B类发生变化。
数据类型
基本类型都有其对应的包装类型。
Integer x=2;
int y=x;
上述例子说明了,基本类型与其对应的包装类型之间的赋值通过自动装箱和自动拆箱实现。
缓存池
new Integer(123)与 Integer.valueOf(123)的区别:
new Integer(123)每次都会新建一个对象。
Integer.valueOf(123)会使用缓存池中的对象,多次调用会得到同一个对象的引用。
典型例子:
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
在Java8中,Integer缓存池的大小为-128~127。
编译器在缓冲池范围内的基本类型自动装箱过程调用valueOf()方法,因此多个Integer实例使用自动装箱来创建并且值相同,那么就会引用相同的对象。
String
String 被声明为 final,因此它不可被继承。
内部使用 char 数组存储数据,该数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。
不可变的好处
- 可以缓存哈希值
因为String的hash值经常被使用,例如String用作Hashmap的key。不可变的特性可以使得hash值也不可变,因此只需进行一次计算。
- String Pool的需要
如果一个String对象被创建过了,那么就会从String Pool中取得引用。只有String是不可变的,才可能使用String Pool。
- 安全性
String经常被作为参数,String不可变才能保证参数不可变。
StringBuffer and StringBuilder
-
标题两者是可变的。
-
线程安全
- String不可变,是线程安全的。
- StringBuilder 不是线程安全的。
- StringBuffer是线程安全的,因为内部使用了synchronized进行同步。
public class test_02 {
public static void main(String[] args){
String s1= new String("aaa");
String s2= new String("aaa");
System.out.println(s1==s2);
String s3=s1.intern();
/*返回值是false*/
System.out.println(s1==s3);
/*返回值是true*/ System.out.println(s1.intern()==s3);
/*返回值是false*/ System.out.println(s1.intern()==s1);
}
}
这里可以知道,intern这个方法是先检查字符串池中是否有内容相同的字符串,如果没有,就会将要检查的内容在字符串池中创建。不是简单地将字符串地址引用,而是一个新的地址。
以双引号形式创建字符串对象实例,会自动将新建的对象放入String Pool中。
String s4 = "bbb";
String s5 = "bbb";
System.out.println(s4 == s5); // true
float与double
1.23
这种字面量默认是double类型,不能直接将1.23直接赋值给float变量,因为这是向下转型。Java不能隐式执行向下转型,这会使得精度降低。
switch
从Java7开始,可以在switch条件判断语句中使用String对象。(不支持long,因为设计初衷是为了少数几个的判断。)
Object方法
- 对于基本类型,==判断两个值是否相等,基本类型没有equals()方法。
- 对于引用类型,==判断两个变量是否引用同一个对象(比较地址),而equals()判断引用的对象是否等价。
equals()方法的实现
package chapter1;
import java.util.Objects;
public class test_04 {
private int x;
private int y;
private int z;
public test_04(int x,int y,int z){
this.x=x;
this.y=y;
this.z=z;
}
public void setY(int y) {
this.y = y;
}
public int getY() {
return y;
}
public boolean equals(Object o){
if (this==o){
return true;
}
if (o==null || getClass() !=o.getClass())
return false;
test_04 that =(test_04) o;
if (x!=that.x) return false;
if (y!=that.y) return false;
return z== that.z;
}
public static void main(String[] args){
test_04 class1=new test_04(1,1,1);
test_04 class2=class1;
class2.setY(2);
System.out.println(class1.equals(class2));
System.out.println(class2.getY());
}
}
hashCode()
hashCode( )返回散列值,而equals( )是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。
在覆盖equals( )方法时应当总是覆盖hashCode( )方法,保证等价的两个对象散列值也相等。
理想的散列函数应当具有均匀性,及不相等的对象应当均匀分布到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来,可以将每个域都当成R进制的某一位,然后组成一个R进制的整数。R一般取31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与2相乘相当于向左移一位。
toString()
默认返回ToStringExample@4554617c ,其中@后面的数值为散列码的无符号十六进制表示。
一般我们都会重写toString( )方法。
clone( )方法
- cloneable
clone( )是Object的protected方法,它不是public,一个类不显式地去重写clone( ),其他类就不能直接去调用该类实例的clone( )方法。
clone( )方法并不是Cloneable接口的方法,而是Object的一个protected方法。Cloneable接口知识规定,如果一个类没有实现Cloneable接口又调用了clone( )方法,就会抛出CloneNotSupportedException。
package chapter1;
public class test_06 {
private int a;
private int b;
@Override
protected test_06 clone() throws CloneNotSupportedException {
return (test_06) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
test_06 class1=new test_06();
//异常
/*test_06 class2= class1.clone();*/
test_06 class2=class1.clone();
}
}
浅拷贝
拷贝对象和原始对象的引用类型引用同一个对象。
public class ShallowCloneExample implements Cloneable {
private int[] arr;
public ShallowCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override
protected ShallowCloneExample clone() throws CloneNotSupportedException {
return (ShallowCloneExample) super.clone();
}
}
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222
深拷贝
拷贝对象和原始对象的引用类型引用不同对象。
public class DeepCloneExample implements Cloneable {
private int[] arr;
public DeepCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override
protected DeepCloneExample clone() throws CloneNotSupportedException {
DeepCloneExample result = (DeepCloneExample) super.clone();
result.arr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result.arr[i] = arr[i];
}
return result;
}
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2
clone( )的替代方案
使用clone( )方法来拷贝一个对象既复杂又有风险,它会抛出异常并且还需要进行类型转换。最好不要去使用clone( ),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。
下面实现了深拷贝:
package chapter1;
public class test_07 {
private int[] arr;
public test_07(){
arr=new int[10];
for (int i=0;i<arr.length;i++){
arr[i]=i;
}
}
public test_07(test_07 original){
arr =new int[original.arr.length];
for (int i=0;i<original.arr.length;i++){
arr[i]=original.arr[i];
}
}
public void set(int index,int value){
arr[index]=value;
}
public int get(int index){
return arr[index];
}
public static void main(String[] args) {
test_07 e1=new test_07();
test_07 e2=new test_07(e1);
e1.set(2,222);
System.out.println(e2.get(2));
}
}
关键字
final
- 数据
声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
- 对于基本类型,final使数值不变;
- 对于引用类型,final使引用不变,也就是不能引用其它对象,但是被引用本身的成员是可以被更改的。
final int x=1;
//不能将x赋值为2,x=2这样是不允许的。
final A y=new A();
y.a=1
//这样使允许的。
- 方法
final声明方法不能被子类重写。
private方法隐式地被指定为final,如果在子类中定义的方法和基类中的一个private方法签名相同,此时子类的方法不是重写基类的方法,而是在子类中定义了一个新方法。
- 类
声明类不允许被继承。
static
- 静态变量
- 静态变量:又称为类变量,类的所有实例都共享静态变量,可以直接通过类名来访问它,静态变量在内存中只存在一份。
- 实例变量:每创建一个实例都会产生实例变量,它与该实例同生共死。
- 静态方法
静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须要有实现。
不能是抽象方法(abstract)
public abstract class A {
public static void func1(){
}
// public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static'
}
静态方法只能访问所属类的静态字段和静态方法,方法中不能有this和super关键字。
- 静态语句块
静态语句块在类初始化时运行一次。
public class A {
static {
System.out.println("123");
}
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
}
}
上面例子输出的是123。说明静态代码块运行且只在第一次初始化类的时候运行。
- 静态内部类
非静态内部类依赖于外部类的实例,而静态内部类不需要。
内部类要在外部类创建完成之后才能创建,但是静态内部类可以不受外部类影响直接创建对象。
package chapter1;
public class test_08 {
class InnerClass{
}
static class StaticInnerClass{
}
public static void main(String[] args) {
test_08 outerClass =new test_08();
InnerClass innerClass =outerClass.new InnerClass();
StaticInnerClass staticInnerClass =new StaticInnerClass();
}
}
- 静态导包
在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。
- 初始化顺序
静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于他们在代码中的顺序。最后才是构造函数的初始化。
public static String staticField = "静态变量";
static{
System.out.println("静态语句块");
}
public String field ="实例变量";
{
System.out.println("普通语句块");
}
public InitialOrderTest(){
System.out.println("构造函数");
}
存在继承的情况下,初始化顺序为:
- 父类(静态变量,静态语句块)
- 子类(静态变量,静态语句块)
- 父类(实例变量,普通语句块)
- 父类(构造函数)
- 子类(实例变量,普通语句块)
- 子类(构造函数)
反射
每个类都有一个Class对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的.class文件,该文件内容保存着Class对象。
类加载相当于Class对象的加载。类在第一次使用时才动态加载到 JVM中,可以使用Class.forname("com.mysql.jdbc.Driver")
这种方式来控制类的加载,该方法会返回一个Class对象。
反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译器该类的.class不存在也可以加载进来。
Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:
-
Field : 可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
-
Method : 可以使用 invoke() 方法调用与 Method 对象关联的方法;
-
Constructor : 可以用 Constructor 创建新的对象。
异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UOCBOBVw-1655646198685)(E:\重要截图\Java\异常继承图.png)]
泛型
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
后面这部分内容比较多,我打算通过另外的章节来详细解释。
参考:https://www.pdai.tech ,尊重作者版权。