0
点赞
收藏
分享

微信扫一扫

java基础知识总结(二)

木匠0819 2022-01-25 阅读 109
java后端

java基础相关,提纲参考javaguide
阅读提示:参考紧随标题

java基本语法

1. java常量与变量

1. 常量与final修饰符

被final修饰的量为常量。如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后引用地址不能变即不能再让其指向另一个对象,但是此地址指向的对象的内容可以变。

  • 常量:程序执行时值不可变,使用前定义用符号(常量名一般大写)代替常量值使用。包括:字符串常量(eg “呵呵”)、整数常量(二进制(0b开头)、十进制、八进制(0开头)、十六进制(0x开头))、小数常量(十进制or科学计数法,默认为double,可写为12.3f单精度节省内存)、字符常量(eg.‘a’、'转义单引号)、布尔常量、空常量(null),final关键字定义常量
  • 常量值:又称为字面常量,它是通过数据直接表示的。

2. 静态常量、成员常量、局部常量

根据作用域分为静态常量、成员常量和局部常量。

public class HelloWorld {
    // 静态常量
    public static final double PI = 3.14;
    // 声明成员常量
    final int y = 10;

    public static void main(String[] args) {
        // 声明局部常量
        final double x = 3.3;
    }
}
  • final修饰静态变量(static修饰的变量)使其为静态常量
  • final修饰成员变量使其为成员常量:必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。final 修饰的成员变量在声明时没有赋值的叫 “空白 final 变量”。空白 final 变量必须在构造方法或静态代码块中初始化。
  • final修饰局部变量使其为局部常量:在使用前被赋值即可,最好是声明时就赋值。

2. 变量

1. java变量分类

  • 静态变量(类变量):static修饰,类中、方法、构造函数或块之外。随类的加载和消失而存在销毁,可以用类名调用也可以用对象调用。无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。静态变量除了被声明为常量外很少使用。静态变量存储在静态存储区,与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为public类型。
  • 成员变量(实例变量):无static修饰,类中、方法、构造函数或块之外。随对象的创建回收而存在和消失,只能由对象调用,在静态方法以及其他类中,使用完全限定名。实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;实例变量具有默认值。变量的值可以在声明时指定,也可以在构造方法中指定。为堆中的对象分配空间时,将为每个实例变量值创建一个槽。实例变量可以在使用之前或之后在类级别中声明。
  • 局部变量:方法、构造函数或块之中。随方法的调用和退出而创建和销毁,访问修饰符不能修饰局部变量(static含全局的意思、final含不可变的意思对于随方法调用创建的局部变量而言没意义),因而没有静态局部变量。只在声明它的方法、构造方法或者语句块中可见;局部变量是在栈上分配的,局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用

3. 静态常量与静态变量

public static final double PI = 3.14;//静态常量
public static int a = 1;//静态变量
  • 作用
    • 静态变量作用:可以被类的所有实例共享,因此静态变量可以作为实例之间的共享数据,增加实例之间的交互性。类的所有实例交互相同的一个变量,变量值可以变。
    • 静态常量作用:相比于静态变量只多了一个final关键字,final的意思是最终,值不可变。如果类的所有实例都包含一个相同的常量属性,则可以把这个属性定义为静态常量类型,从而节省内存空间。例如,在类中定义一个静态常量 PI
  • 加载
    JVM类的加载过程:加载–>验证–>准备–>解析–>初始化。
    • 静态变量:JVM在准备阶段,为类的静态变量分配内存,初始化为系统的默认值 (0 null 等 eg上边代码中a赋值为0)。初始化阶段:静态变量被赋值为指定值(eg a=1)
    • 静态常量:JVM在准备阶段,直接赋值为指定值(eg PI = 3.14)静态常量的初始化完成的比静态变量的早。常量如果属于 “编译期常量”,即在编译期即可确定常量值,则常量值存储在JVM内存中的常量区中,在类不加载时即可访问。如果常量值必须在运行时才能确定,如常量值是一个随机值,也会引起类的加载
    • egpublic static final int FINAL_VALUE_INT = new Random(66).nextInt();

2. java数据类型

java数据类型:基本数据类型、引用数据类型

1. 基本数据类型

8种:

  • 4个整型(默认值大小都为0,有符号二进制补码表示整数)
    • byte:8位,范围:-128~127,用在大型数组中节约空间,主要代替整数byte 变量占用的空间只有 int 类型的四分之一
    • short:16位,范围: − 2 15 至 2 15 − 1 -2^{15} 至 2^{15}-1 2152151
    • int:32位,范围: − 2 31 至 2 31 − 1 -2^{31} 至 2^{31}-1 2312311,整型默认为int
    • long:64位,范围: − 2 63 至 2 63 − 1 -2^{63} 至 2^{63}-1 2632631,默认值为0L. 例long a = 100000L
  • 2个浮点型
    • float:32位,单精度。默认值:0.0f
    • double: 64位,双精度,浮点数默认类型为double。默认值:0.0d
  • 布尔:boolean:默认值false
  • 字符:char:16位 Unicode字符,取值范围:\u0000~\uffff即十进制的0至65535.默认值:\u0000
    可以通过相应封装类(eg.java.lang.Byte)查询二进制位数(Byte.SIZE)、最小值(Byte.MIN_VALUE)和最大值 (Byte.MAX_VALUE)

2. 引用数据类型

对象、数组都是引用数据类型,引用类型默认值为null

3. java修饰符和关键字

包括访问修饰符和非访问修饰符

1. 访问修饰符

  • public:所有类可见
  • protected:同一包内的类和所有子类,若子类和基类在同一个包,则能被包中所有类访问(子类、基类、包中其他类);若子类和基类不在同一个包,则在这样的子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
  • default:同包内可见
  • private:同类可见

2. 非访问修饰符

1. static修饰符

  • 静态变量(类变量):类的所有实例之间共享数据,变量值可以变。随类的加载和消失而存在销毁,可用类名调用,除了被声明为常量外很少使用。
  • 静态方法:独立于对象,可用类名调用,不能使用类的非静态变量
  • 静态常量:和final修饰符一起,类的所有实例都包含一个相同的常量属性,值不可变,eg PI
  • 静态代码块:JVM加载类时执行,用于初始化静态变量,静态代码块只执行一次
  • 静态内部类:
    • 在创建静态内部类的实例时,不需要创建外部类的实例,外部类.内部类 创建静态内部类对象。
    • 静态内部类中可以定义静态成员和实例成员,前者通过外部类.内部类.静态成员名访问,后者通过静态内部类的实例.成员名访问。
    • 静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。
public class Outer {
	int outera = 0;		//外部实例变量
	static int outerb = 0; 		//外部静态变量
    static class Inner {
    	int a = 0;    // 内部实例变量a
        static int b = 0;    //内部静态变量 b
        Outer o = new Outer;	//创建外部类实例
        int a2 = o.outera; 	//内部类通过外部实例访问外部实例变量
        int b2 = outerb;	//内部类访问直接外部静态变量
    }
}
class OtherClass {
    Outer.Inner oi = new Outer.Inner();		//直接创建内部类实例
    int a1 = oi.a;    //通过内部类实例访问内部类实例成员
    int b1 = Outer.Inner.b;		//通过全类名访问内部类静态成员
}

2. final修饰符

不可改变的。

  • 修饰变量:确定后不能重新赋值, final 修饰的实例变量必须显式指定初始值(定义时or构造方法or静态代码块)。对基本类型来说是其值不可变,而对对象属性来说其引用不可再变(即一直引用同一个对象,但这个对象的属性可以发生改变)。和static一起构成静态常量
  • 修饰方法:该功能已经确定只可使用不可扩展修改。出于某些原因,不希望子类重写父类的某个方法。eg Java 提供的 Object 类里就有一个 final 方法 getClass()
  • 修饰类:表示该类是无法被任何其他类继承的

3. absctract修饰符

  • 修饰类: 修饰后不能实例化,修饰目的:对该类扩充。不能和final共同修饰类
    • 如果一个类包含抽象方法,那么该类一定要声明为抽象类
    • 抽象类可以包含抽象方法和非抽象方法。
  • 修饰方法:方法体空,具体实现由子类提供。不能和final or static共同修饰
    • 抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。

4. synchronized 修饰符

修饰方法:同一时间只能被一个线程访问

5. transient 修饰符

序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。

6. volatile 修饰符

volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。一个 volatile 对象引用可能是 null。

3. java变量引用关键字

1. super

作用:

  • 在子类的构造方法中显式的调用父类构造方法
  • 当子类的成员变量或方法与父类同名时,访问父类的成员方法和变量。
1. super在子类构造方法中的使用

父类的构造方法不能被子类继承,子类在实例化对象时:

  • 如果子类的构造器没有显示的调用父类的构造器,则将自动调用父类默认的构造器(无参)相当于默认调用了super()
  • 子类想用父类的有参构造器,必须在子类的构造方法中使用super(参数)表示,而且super必须是子类构造方法中的头一条语句。
  • 如果父类没有不带参数的构造器,且子类的构造器中又没显示的调用父类其他构造器,则Java编译器将报告错误。

2. this

作用:让类中一个方法,访问该类里的另一个方法或实例变量。

  • 访问类中的构造方法:eg 有参构造方法设置private的属性(类外部无法对它们的值进行设置)。
  • 访问成员变量:方法里有个局部变量和成员变量(类属性)同名,方法中要使用同名的成员变量时
  • 访问类中的其他方法
/**
 * 第一种定义Dog类方法
 **/
public class Dog {
    // 定义一个jump()方法
    public void jump() {
        System.out.println("正在执行jump方法");
    }

    // 定义一个run()方法,run()方法需要借助jump()方法
    public void run() {
        Dog d = new Dog();
        d.jump();
        System.out.println("正在执行 run 方法");
    }
}

在run调用类中其他方法jump时使用this关键字

public void run() {
    // 使用this引用调用run()方法的对象
    this.jump();
    System.out.println("正在执行run方法");
}

Java 允许对象的一个成员直接调用另一个成员,可以省略 this 前缀

public void run() {
    jump();
    System.out.println("正在执行run方法");
}

:static 修饰的方法中不能使用 this 引用,因为static方法不依赖于对象,

3. void

java语言是强类型的,方法必须要有一个确定类型的返回值,可将void看成特殊的数据类型(Void类),Void类型不可以继承和实例化,作为方法的返回值时必须返回null。Void也用于无值的Map中,例如Map<,Void>这样map将具Set有一样的功能。

4. 运算符

算术、关系、自增自减、位运算、逻辑(&&与操作时:短路:在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false)、条件运算符(?😃、instaceof()、new

1. 位运算

  • ^异或:如果相对应位值相同,则结果为0,否则为1
  • <<:按位左移
  • >>:按位右移,正数高位补0,负数高位补1
  • >>>:无符号,按位右移补零操作符。

2. instanceof

使用:object instanceof class/interface type

  • 当对象是右边类或子类所创建对象时,返回true;否则,返回false。
  • 左边的对象实例不能是基础数据类型
  • null用instanceof跟任何类型比较时都是false

应用:对象类型强制转换、判断某类的对象是否属于某接口的实例

instance of 实现

1、obj如果为null,则返回false;否则设S为obj的类型对象,剩下的问题就是检查S是否为T的子类型;

2、如果S == T,则返回true;

3、接下来分为3种情况,之所以要分情况是因为instanceof要做的是“子类型检查”,而Java语言的类型系统里数组类型、接口类型与普通类类型三者的子类型规定都不一样,必须分开来讨论。

①、S是数组类型:如果 T 是一个类类型,那么T必须是Object;如果 T 是接口类型,那么 T 必须是由数组实现的接口之一;

②、接口类型:对接口类型的 instanceof 就直接遍历S里记录的它所实现的接口,看有没有跟T一致的;

③、类类型:对类类型的 instanceof 则是遍历S的super链(继承链)一直到Object,看有没有跟T一致的。遍历类的super链意味着这个算法的性能会受类的继承深度的影响

3. new

new运算符实例化一个类对象,实例化:通过给这个对象分配内存并返回一个指向该内存的引用

1. new时发生的过程

java编译器遇到new生成两条字节码指令,new和invokespecial。JVM遇到new指令时

  1. 类加载检查:看指令的参数是否能在常量池中能否定位到一个类的符号引用
    (即类的带路径全名),并且检查这个符号引用代表的类是否已被加载、解析和初始化过,即验证是否是第一次使用该类。 类加载过程见虚拟机的类加载
  2. 类加载检查通过后,JVM在java堆中为新生的对象分配内存:本类和父类的所有实例变量,但不包括任何静态变量。分配方法(取决于垃圾收集器是否进行空间整理):
    • 指针碰撞:内存规整,指针区分被使用和未被使用的内存
    • 空闲列表:JVM维护内存中哪块可用,找到可用内存分配,并更新空闲列表
  3. 实例变量赋默认值:将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值
  4. 设置对象头:(java对象在堆中包括对象头、实例数据、对齐填充)
    • 对象头包括:堆对象的布局、类型、GC状态、同步状态和标识哈希码的基本信息
  5. 执行<init>构造方法设置实例变量值:通过new方法创建对象,new指令后有invokespecial指令,会执行class文件中的<init>方法
2. 显式创建对象的几种方式
1. new关键字
2. 反射机制
  • Class.newInstance():java.lang.Class类中的实例方法,使用被创建对象的类的public的无参构造器。(内部调用Constructor.newInstance())
//java.lang.Class Class 类对象名称=java.lang.Class.forName(要实例化的类全称);
//类名 对象名=(类名)Class类对象名称.newInstance();
Student student2 = (Student) Class.forName("com.dao.Student").newInstance();
//或者
Student student3 = Student.class.newInstance();
- Constructor.newInstance():java.lang.reflect.Constuctor类的实例方法,可以调用有参数的和私有的构造函数
Constructor<Student> constructor = Student.class.getConstructor();
Student student = constructor.newInstance();

3. clone()

赋值一个完全一样的新对象,被克隆的对象的类必须实现Cloneable接口并覆盖Object的clone方法(protected),继承自java.lang.Object类的clone()方法是浅复制

  • 浅复制:本对象的值都一样,对其他对象的引用仍然指向原来的对象。
  • 深复制:被复制的对象所引用的对象也都复制一遍。
3.5 实现深复制

1.让每个引用类型属性内部都重写clone() 方法

//Person类
public class Person implements Cloneable{
    public String pname;
    public int page;
    public Address address;
    public Person() {}
    
    public Person(String pname,int page){
        this.pname = pname;
        this.page = page;
        this.address = new Address();
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person p = (Person) super.clone();
        p.address = (Address)address.clone();
        return p;
    }
    
}
//Address类
public class Address implements Cloneable{
 4     private String provices;
 5     private String city;
 6     public void setAddress(String provices,String city){
 7         this.provices = provices;
 8         this.city = city;
 9     }
10    
14     @Override
15     protected Object clone() throws CloneNotSupportedException {
16         return super.clone();
17     }
18     
19 }

Person类包含Address类型的引用类型,clone()方法中包含对引用对象的复制,但address可能也有引用类型……写不全
2. 反序列化
序列化:对象写到流中便于传输,而反序列化则是把对象从流中读取出来。每个需要序列化的类都要实现 Serializable 接口,如果有某个属性不需要序列化,可以将其声明为 transient,即将其排除在克隆属性之外。 消耗内存

public class Main {

    public static void main(String[] args) throws Exception {
        Person person = new Person("fsx", 18);
        byte[] bytes = SerializationUtils.serialize(person);

        // 字节数组:可以来自网络、可以来自文件(本处直接本地模拟)
        Object deserPerson = SerializationUtils.deserialize(bytes);
        System.out.println(person);
        System.out.println(deserPerson);
        System.out.println(person == deserPerson);
    }

}
4. Objenesis库

org.objenesis 。实例化一个对象而不调用构造器,Spring的ObjenesisCglibAopProxy依赖于Objenesis库。

  1. 使用场景:
  • 构造函数需要参数
  • 有副作用的构造函数
  • 会抛出异常的构造函数
  1. 使用方法:
    Objenesis 的2种实例化对象策略:
  • Stardard: 没有调用任何构造函数
  • Serializable compliant :类似于由java标准序列化实例化的对象。意味着将调用第一个非序列化父类的构造函数。但是,readResolve方法没有被调用,而且我们从不检查对象是否可序列化
Objenesis objenesis = new ObjenesisStd();
ObjectInstantiator objInstantiator = objenesis.getInstantiatorOf(Person.class);//指定实例化的类
Person p1 = objinstantiator.newInstance();//可以实例化一个类的多个实例
Person p2 = objinstantiator.newInstance();
5. java创建对象的4种方式的比较

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RLxPs5gS-1643070262986)(D:\codelernen\八股\java基础\image-20220125075149860.png)]

6. 不调用构造器创建对象的场景
  • 序列化,远程调用和持久化-对象需要被实例化并恢复到特定的状态,而不需要调用代码
  • 代理、 AOP 库和 mock 对象-类可以被子类继承而子类不用担心父类的构造器
  • 容器框架-对象可以以非标准的方式动态地实例化
3. 隐式创建对象
1. 命令行参数

java命令中的每个命令行参数,Java虚拟机都会创建相应的String对象,并把它们组织到一个String数组中,再把该数组作为参数传给程序入口main(String args[])方法。

2. String类型和字符串拼接

String是final类,说明它声明的变量的地址都是不可以修改的,注意是地址已经固定了,改的只是引用指向的值

1.  String a=1”;//JVM 检查在字符串池中是否有"1",无则创建【常量对象】:“1”,而引用a指向了常量对象。如果"1"已经存在则创建了0个对象
2.  String b=new String("2")//若字符串池种无"2",则创建了两个对象,一个是在new String出来的对象“2”,存在堆上;一个是【常量对象】:“2”,b是引用,指向堆上的对象
3.  String c=1”;//这个时候,因为上面的第一条语句已经创建了常量对象1,它已经存在常量池里了,所以没有创建对象,引用c指向了常量对象;
4.  String d = new String("1");//new String肯定是要创建一个对象在堆上的;而常量对象“1”已经在常量池里了,所以这条语句只创建了一个对象;引用d指向了堆上的对象。
5.	String e = "1"+"2"+"3";//创建了一个常量对象“123”,因为java虚拟机在编译时使用常量折叠
6.  String f = "1"+a;//常量对象1上面已经创建过了,a对象也是在上面创建过了,所以这条语句也只创建了一个常量对象11
7.  String g = "3"+a;//常量对象3在常量池中找不到,所以会在常量池中创建常量对象3,以及常量对象31;
2.5 常量折叠

Java 编译期常量 - 云+社区 - 腾讯云 (tencent.com)

编译期常量满足的条件:编译时就能确定值、final类型,基本类型or字符串类型、声明时就初始化、常量表达式初始化

//编译器常量
public  String str1 ="helloworld";
static  String str2= "helloworld";
public static  String str3 = "hello"+"world";

public final int integer1= 1;
static final int integer2= 1;
public static final int integer3 = -1+2;

final char character1= 'a';
3. 字节码生成Class对象

当Java虚拟机加载一个类时,会隐含地创建描述这个类的Class实例.

举报

相关推荐

0 条评论