JavaSE笔记

这篇博客详细介绍了JavaSE的基础和高级部分,包括数据类型、变量、运算符、循环、数组、面向对象、多线程等内容。重点讲解了变量的分类、运算符的使用、异常处理、面向对象的封装、继承和多态,以及多线程基础知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaSE

JavaSE技术范围

JavaSE范围包括Java基础和Java高级部分

基础部分:数据类型、变量、运算符、循环结构、数组、面向对象(以封装、继承和多态展开来讲)

高级部分:多线程基础、常用类于API、枚举类、注解、反射、泛型、IO基础、JVM虚拟机基础

基础部分

数据类型

数据类型分为:基本类型引用类型

基本数据类型很好理解,即8种基本数据类型:byte、short、int、long、float、double、char、float

默认值是依据作为成员变量时,如果不显示初始化时,在类对象实例化过程中,被填充的默认值

类型空间默认值
byte1字节0
short2字节0
int4字节0
long8字节0L
float4字节0.0f
double8字节0.0d
char2字节‘\u0000’
boolean1字节false

引用类型也很好理解,即:类、接口和数组

在平常的代码书写时,外面用到的直接量即字面量,整数字面量整型(int)和小数字面量双精度浮点型(double)

关于char
/**
 * 1.char 占用2个byte空间即 16bit
 * 2.char 可以与int进行转换,毕竟int类型的范围包含char
 * 3.char 的特殊字符转义字符
 * 换行符 \n\r (windows下)
 * 制表符 \t
 * 4.可以用Unicode来进行char表示 Unicode是16进制表示 '\u0020'
 */

变量

变量分类

变量分为静态变量、成员变量和局部变量

静态变量:一般在类加载过程时,静态变量(static)被放入方法区内

成员变量:一般指实例变量,类的实例变量,对象拥有的

局部变量:指的是方法内声明的变量(基本类型、引用类型),一旦方法结束后,所占空间将被GC回收

局部变量的说明

什么是局部变量:局部变量就是在方法体或代码块中声明的变量,方法执行完或代码块执行完,该变量就会被弹出栈。实际上,局部变量是保存在栈中(虚拟机栈),局部变量表。特别注意的是,形式参数也是局部变量,随方法结束一起消亡

成员变量的说明

成员变量如果没有初始化,有其默认值:基本类型为其本身的默认值,引用类型的默认值为null

变量提升

变量提升即,在byte、short、char基本类型作运算的时候,会自动被提升为int类型的变量计算,得到的结果类型仍然为int

byte byte1 = 1;
short short1 = 2;
char c1 = '好';
// byte short char类型会自动提升为int类型进行计算
int result = byte1 + short + c1 ;
隐式转换

在变量赋值的时候,如果“=”左右两边是不同的基本数据类型,小的范围自动被隐式转换为大范围

// 左边范围大,隐式转换
long l1 = 1234; // int 转 long,隐式转换
// 这里用到了缓存机制,127在-128~127内,虽然说字面量式int,但是范围在缓存内,也可以式转换
byte byte = 127; 
缓存机制

Java底层提供了数值类型的缓存机制,对Byte、Short、Integer、Long这四种包装类进行了数值的缓存

缓存何时生效?通过调用包装类的valueOf()方法时,会用缓存,以Byte为例

public final class Byte extends Number implements Comparable<Byte> {
    // 略
    private static class ByteCache {
        private ByteCache(){}

        static final Byte cache[] = new Byte[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }
    
    // 这里用到了缓存
    @HotSpotIntrinsicCandidate
    public static Byte valueOf(byte b) {
        final int offset = 128;
        return ByteCache.cache[(int)b + offset];
    }    
}

Integer的缓存有些特殊,它可以通过JVM启动参数来设置

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];
    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }
    private IntegerCache() {}
}
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

注意

只有Integer的缓存范围可设置,其他三个都是-128~127

Integer i1 = Integer.valueOf(123);
Integer i2 = Integer.valueOf(123);
// i1 == i2 地址是一样的,都为缓存的cache[i],但是如果超出该范围,就不是同一个地址了

直接赋值时,如果是字面量,会先去缓存中获取,缓存中没有时,才进行堆分配,因为valueOf方法是从cache中获得的值,只要在cache范围内,通过valueOf得到的Integer的变量内容的值都是指向一个值而非引用地址

运算符

混合运算
  • 按照运算符我优先级进行运算

  • 如果有String类型参与运算,当到String优先级后,最后的类型都将转为String类型

  • 最终结果以类型范围表示大的为结果

    比如 int + long + double + float 结果为 double

  • 考虑变量提升问题

自运算

自增运算( ++ )或自减运算( -- )运算时,可能会造成范围溢出

byte abyte = 127;
abyte++; // 此时abyte 为-128

注意

如果是 += -= *= /= 自运算符时,不会改变等号左边的数据类型

int a = Integer.MAX_VALUE;
a += 1;
// 这里a还是int类型,然后产生了溢出
System.out.println(a);
逻辑与或非

逻辑与:&,条件都要计算,无论是否有false返回

逻辑或:|,条件都要计算,无论是否有true返回

短路与:&&,只要有false返回,就不计算后面的条件

短路或:||,只要有true返回,就不计算后面的条件

非:!,取反,false取非为true;true取非为false

// 短路与或 str 为null
int a = 1;
int b = 2;
int c = -9;

if(a > b || c<b || str.equals("1") ){
    System.out.println("1");// 短路或时,从左到右依次判断条件,当前仅当至少有一个条件为tre时,则返回,之后的条件不进行判断
}
if(a > b | c<b | str.equals("1") ){
    System.out.println("1");// 正常的或,每个条件都要计算结果,则str会抛出空指针
}

//短路与
if(a > b && c<b && str.equals("1") ){
  System.out.println("1"); // 短语与,从左到右依次判断条件,当遇到第一个false时,返回,后面条件不再进行判断
}
//正常与
if(a > b & c<b & str.equals("1") ){
  System.out.println("1"); // 会抛出空指针,因为每个条件都要去判断是否为true
}
位运算
运算类型描述
&各个位作与操作,包含符号位
|各个位作或操作,包含符号位
~各个位取反,包含符号位
异或^位运算中相同为0,不同为1 ,包含符号位
左移<<空位补0,被移除的最高位丢弃,空缺位补0
右移>>被移位的最高位是0,右移后,空缺为补0;最高位是1 空缺位补1
无符号右移>>>被移位的二进制最高位无论是0还是1,空缺位都用0补
进制和补码
/** 因为int有32位太长 因此,以byte类型为基准 8位
 * 原码
 * 举例:7
 * 正数的原码、反码、补码一样
 * 负数的原码:最高位符号位不变,其他位先减去1然后 按位取反
 * 底层逻辑:计算机底层都是以补码形式保存的
 */
异或幂等
int num1,num2;
num1 = 10;
num2 = 20;

// 异或交换
/*
 基于异或
 令 k = m ^ n
 
 m = m ^ n ^ n

 n = m ^ n ^ m
 */
num1 = num1 ^ num2; // 先异或出一个结果
// num1此时,为 k , 如果k 异或num2,则得到的结果为num1的值,这就是num1赋值给num2了
num2 = num1 ^ num2; 
// num2此时为num1的值
//num1还是那个k,则异或后,得到num2的值,左边时num1则,num2的值赋值给num1了
num1 = num1 ^ num2; 
System.out.println(num1);
System.out.println(num2);

利用异或的幂等性,可以交换两个数值,而不借助第三个变量

循环

循环的要素包括:循环条件,循环体,初始化,迭代部分

循环分为三种:

  • for循环
  • whle循环
  • do-while循环
各个循环的使用场景
  • for循环,包括增强for循环,它适用知道循环次数的情况
  • while循环,它使用不知道循环次数的情况,但是一定有一个终止循环的条件可以达到

数组

  • 数组是一组相同类型的数据集合
  • 访问速度快,通过索引下标直接访问
  • 长度固定,不易扩展和扩容
Arrays工具类

提供了很多和数组相关的方法

例如:查找、排序、复制、比较、填充、Stream流相关等常用的方法,需要我们掌握这个类的使用

面向对象

这里主要将面向对象的一些概括,里面包括很多

内存结构

在这里插入图片描述

  • 堆(heap):此内存区域的唯一目的就是存放对象实例(数组,引用类实例)
  • 虚拟机栈:我们通常所说的栈,用于存储局部变量等,局部变量表存放了编译期可知长度的各种基本数据类型、对象引用(首地址)。方法执行完自动释放
  • 本地方法栈:调用操作系统的C库或其他库
  • 方法区:用于存储被虚拟机加载的类信息、常量池、静态域、即时编译后的代码等数据
局部变量和成员变量

局部变量是方法内的变量,存在局部变量表内

形式参数也是局部变量,就解释了为啥String传入不可变

属性:加载到堆空间(非static,static放入方法区内)

局部变量:加载到栈空间,方法传参时是值传递

值传递

在Java中,参数只有一种传递方式:值传递!引用传递也是传递的地址值!

在实际调用时,方法参数的值是实际参数值的副本

  • 如果参数是基本类型,直接拷贝数据值
  • 如果参数是引用类型,会将实际参数的地址值拷贝给形式参数
包和导入

package 按照项目和模块分级,声明在类的最前面

import 导入的使用

  • 显示导入指定结构下的接口、类
  • import xxx.xx.*表示指定结构下的所有类或接口,*可以理解为通配符
  • 如果用到本包的其他类,不需要用import显示导入
  • 如果用到子包下,则需要import显示导入
  • Java.lang下的结构不需要导入,默认可以使用
封装性

封装是为了追求高内聚、低耦合的目标,封装了类内部的操作细节,不需要外部干涉。隐藏实现细节,对外公开简单的接口,从而提高系统的可扩展性、可维护性

有哪些体现?

  • 私有属性,公共getter/setter
  • 单例模式,构造器私有化,提供公共的得到类的实例的静态方法
  • 不对外暴露的私有方法
  • 静态内部类
  • 权限修饰符,它仅仅只是封装性的表现
继承性

目标:减少代码冗余,提高可扩展性,为多态提供前提

一旦子类B继承了父类A,那么子类B种就拥有了父类A种声明的结构(属性、方法)

private修饰的父类成员变量,子类继承了吗?

实际上,是继承了的,由于封装性的影响,不能在类外部直接访问,最直接的就是通过反射进行访问

public class Person {
    // 父类声明的私有成员
	private String name;

	private void methodA(){
	
	}
}

class Student extends Person{
	// 实际继承了name 和 methodA 
	// 由于封装private的影响,不能再外部直接调用,
}
多态性

理解:一个事物的多种形态

对象的多态性:父类的引用指向子类的实现

方法的多态:对象多态调用方法多态(因为基于继承的特性)

属性多态和方法多态

比如两个类Person、Student

class Person{

    private String name;

    void eat(){
        System.out.println("Person eat");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Student extends Person{

    private String name;

    void eat(){
        System.out.println("Student eat");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

// 测试方法
 public static void main(String[] args) {
    Person person = new Person();
    Student student = new Student();// 动态绑定
    person.eat(); // person eat
    student.eat(); // student eat
    Person p2 = new Student(); // 子类实现
    p2.eat(); // student eat 方法的多态性
}

// 但是属性的调用就是看左边
 public static void main(String[] args) {
    Person person = new Person();
    Person p2 = new Student(); // 子类实现
    p2.name; //person类的name
    Student stu = new Student();
    stu.name;// student的name属性
}

在子类Student的实例对象内存中,有自己的name;而父类Person的实例对象中,也有自己的name,它们是不同的,但是在多态调用中

  • 如果是方法,则运行时确定(动态绑定)
  • 如果是属性,则编译时确定(静态绑定)

多态性使得面向接口编程得以发展,编程更加规范

虚拟方法调用

虚方法调用(Vitural Method Invocation),即多态方法调用

正常方法调用,声明的类型和实现子类一致,这在编译器是确定下来的

Person p = new Person();
Student stu = new Student();
p.eat(); // person的eat
stu.eat(); // stu的eat

虚拟方法调用(多态情况下),动态绑定

// 编译时确定 p类型,但具体的实现是在 运行时确定的 ,所以又称 动态绑定
// p.eat的方法就是student的eat方法了(前提时student覆写了父类person的方法eat)
Person p = new Student();
p.eat();
实例化过程
  1. 产生对象并初始化成员变量的默认值
  2. 对构造方法中的形式参数进行赋值
  3. 构造方法是否有this调用,如果有,走this调用,但至少有一个this是有super的调用的。知道追溯到Object的构造为止

成员属性(非静态)赋值的顺序:

①默认初始化

②显示初始化

③构造初始化

④对象.方法/对象.属性

对象实例化过程

  • 从结果上看:子类继承父类以后,就继承了父类声明的属性和方法。创建子类的对象,在堆空间中就会加载父类声明的属性和方法
  • 从过程上看:子类继承父类后,创建子类时,会直接或间接地调用父类构造器。正因为加载过父类的结构,才可以看到内存中有父类的结构,所以子类才可以调用这些属性

注意

虽然创建子类对象时,调用了父类构造器,自始自终就只创建了一个对象;调用构造器并不是new关键字开辟的空间

在这里插入图片描述

子类实例化时,不会创建父类对象。

向上转型
  • 子类对象转换成父类对象
  • 子类需要和父类是接口实现关系或继承关系
向下转型

父类对象转换成子类对象,由于继承性,子类可能扩展了父类的功能,因此向下转型是不安全的

但是,如果没有多态的支持,向下转型是不成功的,为什么向下转型,因为该对象要用到子类的特性(本身就是动态绑定的子类)

A a = new B();
// 调用没有被B重写过的方法methodOfA()
a.methodOfA();
// 想要用B的独有方法了 向下转型
B b = (B)a;
// 调用B独有的方法,此时就是基于动态绑定的特性
b.methodOfB();

当然在向下转型的时候,最好通过 instanceof 关键字来判断是否是子类的实例

Object类

Object类是所有类的默认父类,如果没有显示的extends,则默认的继承Object

方法名返回类型方法描述
clone()Object复制一个对象,但是类要实现cloneable接口才可以,是一个浅拷贝
equals(Object obj)boolean对象比较方法,默认比较的是地址的hash值
finalize()void调用垃圾回收器,我们不要自己去调用
getClass()Class<?>返回该对象所属类的字节码对象(Class对象)
hashCode()int返回该对象的hashcode值
notify()void唤醒一个在该对象监视器上等待的线程
notifyAll()void唤醒所有在该对象监视器上等待的线程
toString()String返回该对象的字符串表示
wait()voidCauses the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
wait(long timeout)voidCauses the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
wait(long timeout, int nanos)voidCauses the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.
static关键字

可以修饰:属性、方法、代码块、内部类

  • 修饰属性:类中所有对象共享属性、可以通过类.静态成员进行调用或 实例对象.静态成员

    静态变量在JVM方法区内的静态域中,而实例变量在堆中保存,静态变量随类加载而加载

  • 修饰方法:静态方法,可以通过类.静态方法的形式进行调用,但是在静态方法里面,只能调用静态的方法和静态属性。且不能使用this和super关键字

  • 修饰代码块:静态代码块,执行完静态属性初始化完成后。类似于构造块的感觉

final关键字
  • final修饰类,表示不可继承

  • final修饰方法,表示该方法不可复写

  • final修饰成员,表示该成员最多赋值一次,且不可修改。

  • final修饰方法内的变量,表示不可修改,最多赋值一次

abstract关键字

abstract可以修饰类、方法

  • 修饰类:该类为抽象类,不能直接实例化,但是类必须提供一个构造,以便继承的子类进行实例化

  • 修饰方法:该方法为抽象方法,没有实现,具体的实现有子类进行实现,子类继承了必须实现抽象方法。

关于抽象类说明

  1. 继承抽象类的类,如果有抽象方法,则该类必须为抽象的;
  2. 抽象类可以没有抽象方法,不妨碍被abstract修饰;但是有抽象方法声明就要使用abstract关键字
  3. 抽象类起到了很好的模板作用,可以让子类少实现方法
interface关键字

接口,更高级的抽象。接口是一种规范,就是定义的规则;接口和类是两个并列的结构

特点:

  • 可以多实现,间接达到了多继承的效果
  • 实现接口必须实现接口里的所有方法,但是子接口可以不用实现
  • 接口有默认的方法default
  • 接口可以定义static常量
  • 接口中不可以有构造器

JDK7以前

  • 接口内只能定义全局常量:public static final
  • 接口内只能定义抽象方法:public abstract 方法

JKD8

  • 可以定义静态方法 static方法
  • 可以定义默认方法 default 方法
匿名实现类

什么是匿名实现类,不妨这样假设:有一个抽象(接口),现在我们要去继承(实现)这个抽象类(接口),那么我们又不想新定义一个类,直接在代码中定义,类似于这样

抽象类/接口 变量 = new 抽象类/接口(){
    // 抽象类继承子类的定义或接口的实现定义
    // ...
}

new 关键字后面的是抽象类或接口,但是,它的实现是在 {} 内定义的,所以是匿名类

但是这个变量是一个有名字的变量,因此它不是匿名的,而是匿名实现类有名字的对象

匿名对象

顾名思义,匿名的就是没有一个变量或类来接应它,即直接new出来,而不赋值给任何东西。因此是匿名的,只可以使用一次。

new(); //类是可以实例化的类,可以是匿名实现类
代码块

static修饰的{}为静态代码块,在类静态字段初始化后执行,也是类加载的时候执行,仅仅执行一次,可以有多个静态代码块,按顺序执行!

{}声明在类中不加任何修饰的为构造块,先于构造方法执行,可以有多个构造块,按顺序执行

如果声明在方法内,为普通代码块,内部的变量随代码块结束而消亡

内部类
  • 成员内部类,由static 和非 static修饰的class
  • 局部内部类(无修饰符),匿名内部类

什么时候使用内部类,一般成员内部类在各个框架用的比较多,主要是服务于它的主类。如果用static修饰,相当于外部类,但是调用的时候只有在主类内部才可以调用其方法

异常

Java在执行过程中所发生的中断可分为两类

  • Error

    Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重问题,比如StackOverflowError和OOM,一般不编写针对性的代码处理。

  • Exception

    其他编程错误或偶然的外在因素导致的一般性问题。可以使用针对性的代码进行处理,例如:

    空指针访问、文件未找到、网络连接中断、数组下标越界等

异常的分类
  • 编译时异常(也称受检异常)

    在编写代码时,可以出现的异常,必须被处理或者向上抛出

  • 运行时异常(非受检异常)

    在编译器无法确定是否出现,当运行时可能会出现,比如数组下标越界异常等。

异常的体系结构
java.lang.Throwable
	|----java.lang.Error
	|----java.lang.Exception
		|----java.lang.RuntimeException

我们RuntimeException时运行时异常,可以不用捕获,出现时自动抛出,但是其他受检异常必须捕获抛出

throws 和 throw

throws 用在方法上,声明可能产生的异常类型,需要调用者去捕获异常并处理,而throw 是手动抛出异常

定义异常

我们可以继承Exception类或者RuntimeException类

高级部分

多线程

要是服务于它的主类。如果用static修饰,相当于外部类,但是调用的时候只有在主类内部才可以调用其方法

异常

Java在执行过程中所发生的中断可分为两类

  • Error

    Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重问题,比如StackOverflowError和OOM,一般不编写针对性的代码处理。

  • Exception

    其他编程错误或偶然的外在因素导致的一般性问题。可以使用针对性的代码进行处理,例如:

    空指针访问、文件未找到、网络连接中断、数组下标越界等

异常的分类
  • 编译时异常(也称受检异常)

    在编写代码时,可以出现的异常,必须被处理或者向上抛出

  • 运行时异常(非受检异常)

    在编译器无法确定是否出现,当运行时可能会出现,比如数组下标越界异常等。

异常的体系结构
java.lang.Throwable
	|----java.lang.Error
	|----java.lang.Exception
		|----java.lang.RuntimeException

我们RuntimeException时运行时异常,可以不用捕获,出现时自动抛出,但是其他受检异常必须捕获抛出

throws 和 throw

throws 用在方法上,声明可能产生的异常类型,需要调用者去捕获异常并处理,而throw 是手动抛出异常

定义异常

我们可以继承Exception类或者RuntimeException类

高级部分

多线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值