static关键字
在Java中,static
是一个关键字,用于修饰类的成员(变量、方法等)。静态成员属于类本身而不是类的实例,这意味着每个类都有唯一的静态变量副本,所有该类的对象共享这个副本,而不论创建了多少个对象。
有static修饰的代码和没有static修饰的代码的区别:
有static修饰的代码属于类,没有static修饰的代码属于对象
属于类的东西是被所有对象所共享的
初始static关键字
代码:
Test类
public class Test {
public static void main(String[] args){
Person p1 = new Person();
Person p2 = new Person();
p1.name ="李四”;
p1.address=“北京”;
p2.name ="王五”;
p2.address="上海";
System.out.println(p1.tostring());
System.out.println(p2.tostring());
}
}
Person类
public class Person {
public String name ="张三";
public static string address ="保定";
public static void run(string num){
System.out.println("跑了====》"+num+"米");
}
}
拿上面的图片来举例子:
右侧代码写入了一个Person类,而该类中的adress是static类型的,所以adress和其他的存储变量在存储空间上是有所区别的。
对象的创建:
下图是对象创建的时候,流程图是一个怎样的过程。因为有static修饰的adress是属于类的,所以在方法区中有一个静态常量池,这里面是专门存储静态内容的。随着对象的创建,编译器会分配空间并初始化变量。但因为adress是属于类的,所以无论是实例化对象p1或是p2中的哪一个,他们都是静态常量池中的那个adress,所以上面那个代码最终两个对象的adress都是上海。
这里还需要注意的是,代码是按照顺序执行的,如果将p1的打印放在生成p2之前,打印的结果是有所不同的。
static修饰不同的区别
修饰变量:
成为类变量,被所有该类产生的对象所共享的
修饰方法:
成为静态方法
静态方法和非静态方法
静态方法能够直接被类使用,非静态方法不能被main方法所使用
static修饰的方法才能够调用static方法,而main方法是一种静态方法
修饰代码块:
static修饰的代码块在程序运行之前执行,其目的是优化
执行顺序
拿一个案例来举例:
public class Test extends Base {
public Test() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new Test();
}
//static修饰的代码块在ma1n方法执行之前执行,其目的是修饰main方法.
static {
System.out.println("test static");
}
}
class Base {
static {
System.out.println("base static");
}
public Base() {
System.out.println("base constructor");
}
}
运行结果:
原理:
在程序运行过程中,先是扫描.java文件并生成.class,此时main函数提供入口,从这里进入,但当所要进入放入的类有父类的时候,程序先是去扫描父类并生成对应的.class文件,所以在该程序中,先运行父类代码块,输出base static。
父类完毕后接着是子类,运行子类代码块,输出test static。
随后在编译完毕后,进入main函数,new Test();提供构造器,运行构造函数,同样也是按照先父类后子类的顺序所以先后输出base constructor和 test constructor。
流程图如下:
易错点:
static不能修饰局部变量
例题:1.如何代码输出的结果是什么?(D )
public class Test {
public int aMethod( ) {
static int i = 0;
i++;
return i;
}
public static void main (String args[]) {
Test test = new Test();
test.aMethod();
int j = test.aMethod();
System.out.println(j);
}
}
A. 0 B. 1 C. 2 D.编译失败
static不能用于直接声明类
static class MyClass { // 注意:这是错误的,static不能用于直接声明类
int myVariable = 10;
void myMethod() {
System.out.println("Hello");
}
}
static代码块只能执行一次,下面再new对象的时候就和这个代码块没有关系,不会执行了
特性
static修饰符具有如下特性:
● 被static修饰的属性(成员变量)称为静态变量,也叫做类变量;
● 被static修饰的常量称为静态常量;
● 被static修饰的方法称为静态方法,也叫做类方法;
● 被static修饰的代码块叫做静态代码块;
● 被static修饰符的内部类,叫做静态内部类。
紧接上文对static做出小结
static关键字小结:
是什么?
static所修饰的代码属于类,所定义的变量存储在静态常量池中
为什么?
static之所以被设置,是为了方便类能够直接去使用自己的方法和变量
对象:对象是堆里面的一块内存空间
类中的代码以信息的形式存储在方法区当中
对于static方法,建议最好是用类去调用。对象是可以使用类的信息的,所以也可以用对象去调用
举例分析:
1.
A类:
public class A {
public static String name = "admin";
public static void run() {
System.out.println("跑的很快");
}
public static void flay() {
System.out.println("飞的很高");
}
}
Test类:
public class Test {
public static void main(String[] args) {
A a=new A();
a.run();
a.flay();
a.name="ss";
A.name="xx";
A.run();
A.flay();
}
}
运行结果:
2.
A类
public class A {
public static String name="admin";
public String address="保定";
public String toString(){
return "A{"+
"address='"+address+'\'' +
"address='"+address+'\'' +
'}'
;
}
}
Test类
public class Test {
public static void main(String[] args) {
A a1=new A();
A a2=new A();
a1.address="北京";
a1.name="aaaaaaaaaa";
a2.address="上海";
a2.name="cccccccccc";
System.out.println(a1.toString());
System.out.println(a2.toString());
}
}
运行结果:
方法
非静态方法可以调用静态方法,静态方法不可以调用非静态方法。
问题:
如何解决非静态方法不能被静态方法调用的问题?
1.将非静态方法改为静态方法
2.用创建对象的方式去调用
public class A {
public static String name="admin";
public String address="保定";
public String toString(){
return "A{"+
"address='"+address+'\'' +
"name='"+name+'\'' +
'}'
;
}
public static void aaa(){
}
public void bbb(){
}
public static void main(String[] args) {
//将非静态的aaa方法修改成静态的
aaa();
//生成对象,用对象区调用
A p=new A();
p.bbb();
}
}
为什么非静态方法可以调用静态方法?
非静态方法的使用必须要创建对象,而有对象一定是有类的
运行分析(难点):
代码1
public class Demo {
public Demo(String aa) {
System.out.println("====" + aa);
}
static {
System.out.println("11");
}
public static Demo demo = new Demo("+++");
static {
System.out.println("22");
}
}
class Test{
public static void main(String[] args){
Demo demo = new Demo( "----");
}
}
运行分析:
1.main方法在哪里我们就先去扫描那个类----》首先去扫描Test类,发现没有父类,也没有static修饰的代码,那么编译Test类,并且将main方法入栈
2.执行new Demo("----");在执行之前扫描Demo类,发现没有父类,所以编译Demo类,在编译的过程中static代码块按顺序优先执行
3.首先输出的是11
4.执行 public static Demo demo = new Demo("+++");此时会涉及到调用Demo构造方法,所以输出 ===+++
5.输出 22
6.编译完毕,回到main方法当中继续执行new Demo("----");现在可以创建对象了!!也就是进行到了运行阶段
7.通过调佣构造器创建对象:===
运行结果:
关键字this
一、基本概念
在 Java 中,“this” 是一个关键字,它代表当前对象的引用。可以在类的方法中使用 “this” 来引用当前正在调用该方法的对象。
二、为什么可以代表当前对象的引用?
this.属性
class Person {
public String name;
public void run(int name) {
this.name= name;//this.变量
}
}
我们在主方法中这样写道:
Person p1 = new Person();
p1.run("aaa");
Person p2 = new Person();
p2.run("bbb");
这里的this.name相当于指向了实例对象中的变量name,和在局部函数中的参数是两回事。
this.方法
易错点:
static修饰的方法中不能出现this关键字,因为类是构建对象的模板,有类不一定有对象
static修饰的代码被所有的对象共享,所以this可以调用static方法
this() //了解
①thisO)不能在普通方法中使用,只能在构造方法中使用
②this()在构造方法中使用必须是第一条语句
③在一个类下两个构造方法中不能通过this()相互调用
④不能与super()同时使用
二、主要用途
1.区分成员变量和局部变量
当方法的参数或局部变量与类的成员变量同名时,可以使用 “this” 来明确引用成员变量。例如:
class Person {
private int age;
public void setAge(int age) {
this.age = age;
}
}
- 在上述例子中,参数 “age” 与成员变量 “age” 同名,“this.age” 明确表示引用的是成员变量,而不是局部变量。
2.调用构造方法
在一个构造方法中可以使用 “this” 来调用同一类中的其他构造方法,以实现代码的复用和减少重复。例如:
class Person {
private int age;
private String name;
public Person() {
this(0, "Unknown");
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
- 在无参构造方法中,使用 “this (0, "Unknown")” 调用了有参构造方法,初始化了成员变量。
3.作为方法的返回值
“this” 可以作为方法的返回值,返回当前对象的引用,使得可以在方法调用链中连续调用多个方法。例如:
class Person {
private int age;
private String name;
public Person setAge(int age) {
this.age = age;
return this;
}
public Person setName(String name) {
this.name = name;
return this;
}
}
- 可以这样使用:
Person person = new Person().setAge(20).setName("Tom");
三、注意事项
- “this” 只能在非静态方法中使用,因为静态方法是属于类的,而不是属于特定的对象。
- 在构造方法中调用其他构造方法时,“this” 语句必须放在构造方法的第一行。
关键字 final
一、概述
在 Java 中,final
关键字用于限制变量、方法和类的行为。它表示不可改变或最终的状态。
二、作用于变量
final所修饰的变量叫做常量(常量是只能赋值一次的变量)
public class Test01 {
public static void main(String[] args) {
final int NUM= 10;
// NUM= 20; // 会报错,因为 final 变量不能被重新赋值。
System.out.println("Final variable num: " + num);
}
}
注意:常量的变量名要大写
按照作用域区分:
1.全局变量:final修饰的全局变量必须赋值(因为final只能赋值一次)
2.局部变量:
按照类型区分:
-
1基本类型变量
- 当用
final
修饰基本类型变量时,该变量一旦被初始化,其值就不能再被改变。 - 例如:
final int num = 10;
,之后任何试图改变num
值的操作都会导致编译错误。
- 当用
public class FinalPrimitiveExample {
public static void main(String[] args) {
final int num = 10;
// num = 20; // 编译错误,不能重新赋值
System.out.println("Final primitive variable: " + num);
}
}
-
2.引用类型变量
- 对于引用类型变量,
final
表示该引用不能再指向其他对象。但被引用的对象本身的内容是可以改变的。 - 例如:
final List<Integer> list = new ArrayList<>();
,不能再将list
指向另一个新的集合对象,但可以对list
中的元素进行添加、删除或修改操作。
- 对于引用类型变量,
public class Test{
public static void main(String[] args) {
final int[] arr1 = new int[]{1,2,3};
final int[] arr2 = new int[]{12,22,32};
final int a = 10;
public void fun(){
a=11;//error
}
public void changge(){
arr1=arr2;//error
}
}
}
三、作用于方法
class Test02 {
final void finalMethod() {
System.out.println("This is a final method and cannot be overridden.");
}
}
class SubClass extends Test02 {
// 不能重写父类的 final 方法
// void finalMethod() {}
}
- 当一个方法被声明为
final
时,这个方法不能在子类中被重写。- 这可以确保该方法在继承体系中的行为是固定的,不会被意外修改。
- 例如:
class Parent { final void method() { // 方法体 } }
,如果有一个子类Child
试图重写method
方法,会导致编译错误。
四、作用于类
final class Test03{
public void display() {
System.out.println("This is a final class.");
}
}
// class SubFinalClass extends Test03{} // 会报错,因为 final 类不能被继承。
- 当一个类被声明为
final
时,这个类不能被继承。- 这可以确保该类的实现是完整的,不会被其他类修改或扩展。
- 例如:
final class FinalClass { // 类的成员 }
,任何尝试继承FinalClass
的类都会导致编译错误。
五、优点
- 安全性
- 确保变量的值或方法的行为不会被意外改变,增强了程序的安全性和稳定性。
- 性能优化
- 在某些情况下,编译器可以进行优化,因为它知道被
final
修饰的内容是不可变的。
- 在某些情况下,编译器可以进行优化,因为它知道被
六、注意事项
- 谨慎使用
- 过度使用
final
可能会导致代码灵活性降低,在设计时需要权衡可维护性和安全性。
- 过度使用
- 理解引用类型的限制
- 对于
final
引用类型变量,要清楚只是引用不能改变,而被引用对象的内容可能是可变的。
- 对于
七、同样是不可修改,final关键字和private看似有些相似,他们又有什么区别?
一、作用对象不同
-
final
:- 可以修饰变量、方法和类。
- 修饰变量时表示变量的值不可改变;修饰方法时表示该方法不能被重写;修饰类时表示该类不能被继承。
-
private
:- 主要用于修饰成员变量、成员方法和内部类。
- 表示被修饰的成员只能在当前类中被访问,对其他类不可见。
二、限制范围不同
-
final
:- 限制的是可变性和可继承性等方面的行为。
- 对于变量,限制了其值的更改;对于方法,限制了其在子类中的重写;对于类,限制了其被继承。
-
private
:- 主要限制的是访问权限。
- 确保被修饰的成员只能在定义它们的类内部使用,外部类无法直接访问。
三、继承和重写方面
-
final
:- 阻止方法被重写和类被继承。
- 如果一个方法被声明为
final
,子类不能重写该方法;如果一个类被声明为final
,则不能有子类。
-
private
:private
方法不能被重写,因为它们在子类中不可见。但是,这并不是因为明确禁止重写,而是由于访问限制导致的。同时,private
修饰的类成员不会影响类的继承关系,子类可以继承父类中private
修饰的成员变量和方法,只是不能直接访问。
四、使用场景不同
-
final
:- 当你希望确保某个变量的值在初始化后不再改变,或者某个方法的行为在子类中不被修改,或者一个类不应该有子类时,可以使用
final
。 - 例如,在定义常量时可以使用
final
修饰变量。
- 当你希望确保某个变量的值在初始化后不再改变,或者某个方法的行为在子类中不被修改,或者一个类不应该有子类时,可以使用
-
private
:- 当你希望隐藏类的实现细节,只暴露必要的公共接口时,可以使用
private
修饰成员变量和方法。 - 比如,一些内部的辅助方法或者不希望被外部类直接访问的变量可以使用
private
修饰。
- 当你希望隐藏类的实现细节,只暴露必要的公共接口时,可以使用