Java语法基础:final和static关键字

语法基础

关键字final

意思是最终的、不可变的

修饰变量

final修饰变量时,变量必须初始化,并且不能被修改。初始化可以定义final变量时直接初始化或者在构造函数中初始化

修饰局部变量

修饰方法

final修饰方法则该方法不能被子类重写

修饰类

final修饰一个类,则该类不能被继承

扩展

表示字符串的三种类型String、StringBuffer、StringBuilder都是final类型的类,所以都不允许继承

总结

  • final属性山可以声明的时候同时直接赋值或者在构造器中赋值

  • final临时变量,可以声明的同时赋值或者在第一次使用之前进行赋值

    • 注意:final类型的变量一旦赋值则不允许修改,但是如果是复杂类型是不允许修改地址,但是可以修改属性

  • final方法表示这个方法不允许在子类中重新定义(覆盖 \ 重写)

  • final类表示这个类不允许被修改

实现类不能被继承的方法

private私有构造器

单例模式

模式:是由专家总结出来的在某种情况下解决某些问题的最佳解决方案,是思想、是知识,是一种抽象的内容

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点

主要解决:一个全局使用的类频繁地创建于销毁

何时使用:当你想控制实例数目,节省系统资源的时候

单例模式有 多种写法,最常见的是懒汉模式和饿汉模式

饿汉单例模式

优点:没有枷锁,执行效率会提高

缺点:类加载是就初始化,浪费内存

编程实现:

  • 私有构造器

  • 静态的私有属性

  • 公共的静态方法

懒汉单例模式

懒汉式:主要针对对饿汉式中不管是否需要使用对象所引起的内存浪费

优点:采用延迟出处理的方式,所以比较节约内存

缺点:在多线程不能正常工作

静态关键字static

  • 用于修饰成员

  • 随着类加载,随着类消失

  • 优先于对象,用类名直接访问

静态属性

static属性式当前类的所有对象共有的共同属性(只有一个,而普通属性各个对象都有自己的,相互隔离),任何一个当前类对象修饰这个属性,所有其他类对象的这个属性都会受影响

典型应用:统计A1类的构建次数

执行顺序

静态属性---属性---构造器

访问的方式

可以使用“类名.静态属性名”或者“对象名.静态属性名”的方式进行访问。【范围限制】

定义常量

命名规则:名称全大写,下划线分词

  • 声明语法:public static final double MIN_NUMBEAR = 0.1;

  • 声明的同时直接进行初始化

  • 先声明后在static静态块中赋值

静态方法

  • 因为可以直接使用“类名.方法名”的形式直接调用静态方法,静态方法执行时很有可能并没有构建对象,所以在静态方法中不允许使用this/super之类用于指定对象的关键字

  • 当然在静态方法中允许创建对象,并调用对象方法

  • 静态方法只能访问静态成员,不能直接访问非静态成员

静态块

  • 类在执行时需要通过一个叫做类加载器的组件将程序加载到内存中,类在运行时一般不会发生变化,所以类不会频繁加载,在整个运行过程中只加载一次,而且常驻内存

  • 静态块在类加载完毕后自动执行,而且只执行一次

非静态块

非静态块在类内且在所有的方法之外,非静态块并不会在类加载后自动执行,而是在构建当前对象时自动执行。new一次则会执行uici,执行实际在构造器之前执行

考试点

  • 当类加载完毕后会自当优先处理static属性和static块,这两个优先级是相同的,所以谁在前先处理谁

  • new对象时,处理非静态属性和非静态块,这两个优先级相同的,所以谁在前就先处理谁

  • 最后执行构造器

使用注意事项

  • 静态方法只能访问静态成员,静态有访问局限性

  • 静态方法中不能有this/super关键字

  • 主函数是静态的

什么时候使用静态成员

  • 当成员变量的数据对各个对象都是相同时,可以使用static修饰的,让多个对象共享

  • 方法如果访问了特有数据(非静态成员变量),该函数是非静态的。方法如果没有访问特有数据,那么该方法就是静态修饰

  • 如果类中的功能都是静态的,那么该类创建对象是没有意义的,所以构造方法需要私有化

方法中的可变个数的参数

语法:数据类型... 变量名。-必须作为最后一个参数出现

具体处理过程中实际上是按照数组的方法进行处理,而且数组不会为null

public void pp(int... kk){
    if(k1.length > 0){
        for(int i = o;i < k1.length;i++){
            System.out.println(k1[i]);
        }
    }
}

void pp(Object... arr)可变长参数可以是Object[]数组

注意:一个方法的最后一个位置只有一个,所以方法中的可变个数的参数只能有一个

static相关问题

父类中定义的静态方法(类方法)是否可以通过“子类名.静态方法名”的格式进行调用?

可以,因为继承的特性

public class Test1{
    public static void main(String[] args){
        Son.pp();
    }
}
    //输出结果为  Fa.pp
​
class Fa{
    public static void pp(){
        System.out.println("Fa.pp");
    }
}
       
 class Son extends Fa{}  

父类中的静态方法(类方法)可以在子类中进行覆盖定义(重写)

但是方法签名必须一致,方法必须为static定义

public class Test1{
    public static void main(String[] args){
        Son.pp();
    }
}
    //输出结果为 Son.pp
​
class Fa{
    public static void pp(){
        System.out.println("Fa.pp");
    }
}
        
 class Son extends Fa{
     public static void pp(){
         System.out.println("Son.pp");
     }
 }  

特殊点:静态方法也允许通过对象进行调用

public class Test1{
    public static void main(String[] args){
        Fa ff = new Son();
        ff.pp();  //注意这里调用的是父类中定义的方法,不是子类中覆盖定义的新方法
//      Son.pp();   这里调用的是子类覆盖定义的新方法
    }
}
//如果使用构建对象的方式进行调用,则使用谁声明则调用谁的静态方法---反例
//使用类名调用,哪个类调用就是哪个类的方法
class Fa{
    public static void pp(){
        System.out.println("Fa.pp");
    }
}
        
 class Son extends Fa{
     public static void pp(){
         System.out.println("Son.pp");
     }
 }

静态块没有覆盖定义的概念,如果父子类中都定义了静态块,一定是先执行父类中的静态块,然后运行子类中的静态块

  • 加载顺序为先父后子---先执行父类中定义的静态属性和静态块

  • 然后执行初始化操作---先处理父类成员属性和非静态块,在执行夫类构造器;后执行子类的定义内容

静态导入

在一个类中反复使用到某个类的静态方法,如果使用静态导入则在当前类中不需要再写类名称

在jdk5中提供导入了静态方法import语句

  • 语法:import static java.lang.Math.*; 导入Math类中的所有静态方法

  • 注意要求使用JDK1.5+版本,否则编译不通过

import static java.lang.Math.*; 
​
public static void main(String[] aegs){
    double a1 = -12.3456;
    System.out.println(abs(a1));
    System.out.println(cos(a1));
    System.out.println(sin(a1));
}

成员实现细节

JVM主要包括三块内存空间,分别是栈内存、堆内存和方法区

 

Java虚拟机包含类装载器系统、执行引擎、运行时数据区、本地方法接口和垃圾收集模块

  • 类装载器子系统:根据给定的权限定类名(如: java.lang.Object)来装在class文件到运行时数据区域的方法区中

  • 执行引擎:执行字节码或执行本地的方法

  • 运行时数据区:就是常说的JVM的内存、堆、方法区,虚拟机栈,本地方法栈,程序计数器

  • 本地方法接口:与本地方法库交互,作用就是为了融合不同编程语言为Java所用,它的初衷是融合C/C++程序

首先通过编译器把Java代码转换为字节码,类加载器再把字节码加载到内存中(运行时数据的方法区内),而字节码文杰只是JVM的一套指令集规范,不能直接交给底层系统去执行,所以需要特定的命令解析器执行引擎将字节码翻译成底层执行系统,再交给CPI去执行,而这个过程需要调用其他语言的本地库接口来实现整个程序的功能

 

方法的代码片以及整个类的代码片段被存储到方法去内存中,再类加载的时候这些代码片段会被载入。方法区与Java堆一样,是各个线程共享的内存区域,它利用已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据

  • JDK8完全废弃了永久代的概念,改用在本地内存中实现的元空间来代替,JDK7中永久代还剩余的内容(主要是类型信息)全部转移到元空间中。异常情况:方法区无法满足新的内存分配需求时,将抛出OOM异常

  • 运行时常量池时方法区的一部分,并非预置入class文杰 中常量池的内容才能进入 方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的是String类的intern()方法

方法执行使用的是栈,该方法需要的内存空间在栈内存中分配,称为压栈。方法执行结束后,该方法所占用的内存空间自动释放,称为弹栈,栈中主要存储的是方法体当中的局部变量

引用和 指针的区别

引用也叫句柄,类似于指针,但是和指针是不同的。指针是一个存放地址的变量,使程序员可以灵活的访问内训,由于可以对指针进行任意的运算操作,所以给程序带来了安全隐患和意想不到的结果。引用继承了指针节省内存的优点,但是限制了对地址的操作,他是安全的。Java中所有的变量都是一个引用,java中没有指针的概念。

在程序执行过程中使用new运算符创建的Java对象,存储在堆内存中。对象内部有实例变量,所以实例变量存储在堆内存当中

变量分类:

  • 局部变量,方法体中声明

  • 成员变量:方法体外类内声明

    • 实例变量,没有static修饰,各个对象相互隔离

    • 静态变量,有static修饰,这个类对所有对象共享

    • 静态变量存储在方法区内存中

  • 三块主要内存中变化最频繁的是栈内存,最先有数据的是方法区内存,垃圾回收期主要针对的是对内存

自动垃圾回收机制GC什么时候会将java对象的内存回收

  • 当堆内存中的对象成为垃圾数据的时候会被回收

  • 什么时候堆内存对象会编程垃圾?

    • 引用计数法:为每个对象创建一个引用计数器,有对象引用时计数器加1,引用被释放时计数器-1,当计数器为0时就可以被回收。它有一个缺点就是不能解决循环引用的问题

    • 可达性算法(引用链发):从GC Roots开始向下搜索,搜索所走过的路径称为引用链,当一个对象都GC Roots没有任何引用链相连时,则证明对象是可以被回收的

总结:

  • 栈内存存储基本类型的变量和对象的引用变量

  • 堆内存用于存放有new创建的对象和数组。每new一个对象就在堆内存中开辟一个新的内存空间存储此实例对象

  • Person p = new Person()执行new命令是程序执行两步:a:在堆内存中开辟一段空间,存储new出来的对象;b:在栈内存中添加一个变量p,p中存放的是对象在内堆内存中开始存放处的物理地址

  • p = null;执行此步骤的时候程序只是更改栈内存中的p变量所保存的地址,把地址指向null,而并没有操作堆内存(把p所指向的对象实例清空回收)

  • 无论是形参或者实参,执行XXX = null;操作时都是把XXX变量栈中存储的地址改为指向null的地址。不需要堆中的数据

具体问题

由类创建一个对象,JVM内存中发生了哪些事情

MyClass mc = new Myclass();以这条语句为例,Myclass mc = 使虚拟机栈中生成了一个指向Myclass对象的地址;new Myclass ()则在堆中分配了对象mc成员变量的空间

栈和堆的区别

  • 管理方式:栈自动释放,堆需要GC

  • 空间大小:栈比堆小

  • 碎片相关:栈产生的碎片远小于堆

  • 分配方式:栈支持静态和动态分配,而堆仅仅支持动态分配

  • 效率:栈的效率比堆高

方法的问题

  • 无参无返(没有参数列表,没有返回值)单纯的作为功能代码的聚合使用便于功能复用

  • 无参有返(没有参数列表,有返回值)

  • 有参无返(有参数列表,没有返回值)适用于功能需要根据参数来进行计算的情况,但是计算的最终结果又无需返回处理

  • 有参有返(有参数列表,有返回值)适用于功能需要根据参数进行计算的情况,而且最终的结果需要返回处理

方法的形参和实参

  • 形参:是定义在方法声明上,用于指定该方法需要传递的参数类型的

  • 实参:是调用方法时,实际传递的参数值

方法参数 传递

  • 基本数据类型作为参数值传递:传值传的是值的内容,来到另一个方法空间后,这个值和之前没有任何关系

  • 引用数据类型作为参数传递:传值传的是对象在堆的地址值,所以两个内容指向了同一片空间是相互影响的

基本数据类型的对象缓存

在不可变类Integer类定义中查看源代码可以发现一个定义

private static class IntegerCache{}  这实际上就是Integer的cacha
Integer num1 = 12;
Integer num2 = 12;
System.out.println(num1 == num2);  //结果为true
​
​
Integer num1 = 128;
Integer num2 = 128;
System.out.println(num1 == num2);  //结果为false
​
Integer num1 = -129;
Integer num2 = -129;
System.out.println(num1 == num2);  //结果为false

是因为Integer中包含一个缓存池,缓存值为-128 --- 127之间

  • 定义Integer k1 = 12 是先在缓存池中查找12这个对象,如果有则直接使用

  • new Integer(12) 一定会引发对象的创建,而不管缓存池中是否包含这个对象

  •  Integer num1 = 12;
            Integer num2 = 12;
            System.out.println(num1 == num2);  //true num1、num2都是引用类型,所以==比较的是地址
            Integer num3 = new Integer(12);
            System.out.println(num1 == num3)  //false

Integer\Long\Short\Byte中都有缓存池、charcter中也有缓存池,boolean只有true/false两个

字符串缓存池

String中包含一个缓存池,当使用某个字符串对象时会首先在缓存池中进行查找,如果存在则直接返回这个对象的地址;如果不存在则会在缓存池中进行创建,创建完成后返回地址

        String s1 = "abc";
		String s2 = "abc";
		String s3 = new String("abc");
		System.out.println(s1 == s2); //true
		System.out.println(s1 == s3); //false

注意:如果通过字符串拼接所得的内容和某个字符串内容一致,但是地址不同

		String s2 = "abc";
		String s4 = "ab";
		String s5 = "c";
		String s6 = s4 + s5;
		System.out.println(s2 == s6); //false

 

装箱/拆箱

值类型自动转换成他对应的类类型-autoboxing 、类类型自动转换成它所对应的值类型-unboxing

  • 装箱 如:Integer a = 9;

  • 将一个对象自动转换成基本数据类型就叫拆箱 int c = new Integer(9);

面试题

1.使用static修饰属性或方法后,属性和方法有什么特征?

解析:static修饰符是一个非常罕见并重要的修饰符,称为静态。静态不是指值不能改变,。static常常用来修饰类的属性或者方法。当一个属性或者方法和对象没有关系,或者说是任何对象共享的时候,那么就应该用static进行修饰。例如某类中的计数器,用来计算实例的个数。那么这个计数器属性就是所有对香港共享的属性,就应该用static修饰。又例如Math类中的abs(int),用来返回参数的绝对值,这个方法和Math类的对象没有关系,Math类对象可以共享这个方法,那么这个方法就可以用static修饰。

参考答案:static修饰属性或方法后,属性和方法不属于某个特定的对象,而是所有共享,也可以说是static成员不依赖于对象,在类加载时就被初始化。static修饰的属性和方法,可以直接使用类名调用,而不用先实例化对象在调用。

2.使用final修饰属性后,属性有什么特征?

解析:属性可以是变量也可以是常量,如果是常量,就需要使用final修饰。如果使用final修饰某个属性,那么该属性值一旦被赋值,就不能被修稿,实际中常常有个这样的码:private static finsal ERROR = 0;也就是说,常常会有生命静态的常量。静态常量的命名非常特殊,往往都是用大写字母,如果包含多个单词,每个但粗之间使用下划线连接,静态常量的意思是,该类所有的对象都拥有一个不变的常量ERROR,作为0.API中很多类都定义了很多的静态常量,使用时直接使用该类名调用即可

参考答案:final修饰属性后,属性就成为了一个常量,常量只要被赋值,就不能被改变

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值