Java的static关键字
Java关键字是什么?
摘自百度百科
Java关键字是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字,还有特别意义的变量。Java的关键字对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数。
常用的关键字如下图
Java关键字.png
针对于上述关键字,我们抽出几个比较重要的来唠唠
修饰字符类型关键字
static
在Java中,static表示“静态的”,它也是一种修饰符,可以修饰属性、方法、代码块和内部类。
一般情况下,static修饰的变量,方法,代码块。都跟随类的生命周期,它们所属的JVM内存也都是处于共享内存
变量:
静态变量(或称为类变量),指被 static 修饰的成员变量;类的静态变量在内存中只有一个,Java虚拟机在加载类的过程中为静态变量分配内存,静态变量位于方法区,被类的所有实例共享。静态变量可以直接通过类名被访问。静态变量的生命周期取决于类的生命周期,当加载类的时候,静态变量被创建并分配内存,当卸载类的时候,静态变量被销毁并撤销内存。
实例变量,指没有被 static 修饰的成员变量;类的每个实例都有相应的实例变量。每创建一个类的实例,Java虚拟机就会为实例变量分配一次内存,实例变量位于堆区中。实例变量的生命周期取决于实例的生命周期,当创建实例的时候,实例变量被创建并分配内存,当销毁实例的时候,实例变量被销毁并撤销内存。
局部变量,指的是在一个方法的内部或者方法的一个代码块中声明的变量,前者的作用域就是整个方法,后者的作用域则是这个代码块(代码块就是{}以内的代码)
class Person() {
private String name; // 实例变量
private Integer age; // 实例变量
private static String country = "China"; // 静态变量
public person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
当我们使用这个类创建实例的时候
public static void main(String[] args) {
Person p1 = new Person("zhangfei", 30);
Person p2 = new Person("guanyu", 35);
System.out.println(p1.name);
System.out.println(Person.country);
}
内存分配如下:
Java_static内存分配.jpg
方法:
- 静态方法是使用 static 关键字修饰的成员方法,它属于类,而不是类的实例。
- 静态方法可以直接通过类名来调用,也可以通过类的对象来调用,但一般推荐使用类名来调用。
- 静态方法只能访问类的静态变量和静态方法,不能访问类的实例变量和实例方法,也不能使用 this 关键字。
- 静态方法通常是一些工具方法,不需要依赖于对象的状态,例如 Collections 类、Math 类等都包含了很多静态方法。
# 接上面的Person类
class Person() {
……
public String printName() {
System.out.println(this.name);
}
public static printCountry() {
System.out.println(Person.country);
}
}
实际调用
public static void main(String[] args) {
Person p1 = new Person("zhangfei", 30);
Person p2 = new Person("guanyu", 35);
p1.printName();
Person.printCountry(); // 使用类名调用
p1.printName(); // 使用对象调用,推荐使用类名调用
}
代码块:
静态代码块,在Java类中,使用一对大括号包围起来的若干行代码被称为一个代码块,用static关键字修饰的代码块称为静态代码块。
当类被加载时,静态代码块会执行,由于类只加载一次,所以静态代码块只会执行一次。在程序中,通常会使用静态代码块来对类的成员变量进行初始化
# 接上面的Person类
class Person() {
static{
System.out.println("开始加载类,执行静态代码块");
}
}
构造代码块
java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字)
# 接上面的Person类
class Person() {
{
System.out.println("开始加载类,执行构造代码块");
}
}
这里要梳理清楚执行顺序
静态代码块>构造代码块>构造函数>普通代码块
我们可以实际模拟运行一下
public class codeBlock {
static {
System.out.println("静态代码块");
}
{
System.out.println("构造代码块");
}
public codeBlock(){
System.out.println("无参构造函数");
}
public void sayHello(){
System.out.println("普通代码块");
}
public static void main(String[] args) {
System.out.println("执行了main方法");
new codeBlock().sayHello();
System.out.println("---------------------------");
}
}
执行结果如下:
静态代码块
执行了main方法
构造代码块
无参构造函数
普通代码块
---------------------------
嵌套类
嵌套类又成为内部类,内部类就是一个定义在一个类里面的类,里面的类可以理解为(寄生),外部类可以理解成(宿主)
分为静态内部类和非静态内部类;
- 静态内部类是指被声明为static的内部类,
- 非静态内部类是指没有被声明为static的内部类
实例化:静态内部类的实例化不依赖于外部类的实例,可以直接通过类名访问;而非静态内部类的实例化必须依赖于外部类的实例,只能在外部类的实例方法中创建。
关系:静态内部类与外部类没有任何联系,只是被包含在外部类中;而非静态内部类可以访问外部类的成员和方法,并且可以使用外部类的引用来访问外部类的成员。
静态内部类不需要依赖外部类的实例而可以被实例化。静态内部类通常用于封装与外部类紧密相关的一组静态方法或常量
public class OuterClass {
public static class InnerClass {
// 静态内部类中可以定义一组静态方法或常量
public static final int MAX_VALUE = 100;
public static int add(int x, int y) {
return x + y;
}
}
}
public class OuterClass {
private int outerVar;
public OuterClass(int outerVar) {
this.outerVar = outerVar;
}
public void outerMethod() {
int localVar = 10;
// 非静态内部类
class InnerClass {
public void innerMethod() {
System.out.println(outerVar); // 访问外部类的实例变量
System.out.println(localVar); // 访问局部变量
}
}
InnerClass inner = new InnerClass();
inner.innerMethod();
// 静态内部类
static class StaticInnerClass {
public static final int MAX_VALUE = 100;
public static void staticInnerMethod() {
System.out.println("This is a static inner class.");
}
}
StaticInnerClass staticInner = new StaticInnerClass();
staticInner.staticInnerMethod();
}
}
非静态内部类:InnerClass可以访问外部类的实例变量outerVar和局部变量localVar,因此适合在需要访问外部类实例的情况下使用**,例如实现一个事件监听器。
Outer.Inner in = new Outer().new.Inner()
静态内部类:StaticInnerClass没有访问外部类实例的需求,只需要实现一些独立的功能,因此适合在不需要访问外部类实例的情况下使用,例如实现一个工具类。同时,静态内部类的实例化不依赖于外部类的实例,可以直接通过类名访问,使用起来更加方便。和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已
Outer.Inner in = new Outer.Inner();
一道有意思的面试题
class People() {
private int heartbeat = 150;
public class Heart {
private int heartbeat = 110;
public void show() {
int heartbeat = 80;
System.out.println(heartbeat);//80
System.out.println(this.heartbeat);//110
System.out.println(People.this.heartbeat);//150
}
}
public static void main(String[] args) {
new People().new Heart().show();
}
}
输出结果
80
110
150
匿名内部类(重要)
匿名内部类可以使你的代码更加简洁,你可以在定义一个类的同时对其进行实例化。它与局部类很相似,不同的是它没有类名,如果某个局部类你只需要用一次,那么你就可以使用匿名内部类
但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
匿名类是不能有名字的类,它们不能被引用,只能在创建时用New语句来声明它们。
匿名类的声明是在编译时进行的,实例化在运行时进行,这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
为什么在Java中需要内部类?
我们先看一下不使用匿名内部类来实现抽象方法的时候
abstract class Person {
public abstract void eat();
}
class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}
用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用
但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
这个时候就引入了匿名内部类
引入匿名内部类之后
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
可以看到,我们直接将抽象类Person中的方法在大括号中实现了
这样便可以省略一个类的书写,并且,匿名内部类还能用于接口上
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现
最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口
Thread类的匿名内部类实现
public class Demo {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start();
}
}
Runnable接口的匿名内部类实现
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
Thread t = new Thread(r);
t.start();
}
}
这篇文章介绍匿名内部类使用场景,比较通俗易懂。java匿名内部类总结
匿名内部类总结:
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。