草庐IT

Java中static关键字和代码块的学习

牛牛要坚持 2023-04-05 原文

本文介绍了Java中static关键字的使用,即静态成员变量和成员方法以及使用,静态与非静态成员变量和方法的对比总结
Java中的代码块介绍与最后结合代码块和构造方法后的初始化代码执行顺序的练习

static和代码块的学习

三.认识static关键字

static关键字也就是静态的意思,在java中可以修饰成员变量,成员方法,代码块…

1.static修饰成员变量

static修饰的成员变量,称为静态成员变量或类成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。

[权限修饰符] static 数据类型 变量名 = [就地初始化值];

示例:

public class Student{
public static String school1="清华大学"; //直接就地初始化 在加载类时申请空间后会直接赋值
//static修饰 成员变量为静态成员变量(类变量)存放在方法区 是加载类时候就申请了空间 不属于对象 是属于类的 也就是所有通过类实例化对象共用的一个变量
}

此时定义的school1 为这个类里的静态成员变量,是属于类的,通过这个类实例化的所有对象都共享这一个变量.

访问静态成员变量的方法:

 System.out.println(Student.school1);  
 //被static修饰的成员变量属于类变量 直接通过类名.变量 访问 最推荐这种
   System.out.println(student1.school1); 
  // 也可以通过student1访问school 不推荐 引用变量也属于Student类型也可以访问类变量
   Student student2=null;
 System.out.println(student2.school1);
  //可以看出 student2引用变量里是null表示没有指向任何对象 但是可以输出清华大学,student2不指向对象不妨碍其访问类对应的类变量
  System.out.println(((Student)null).school1);
  //甚至可以把null空引用强转为Student类 此时可以访问Student类里的成员变量

【静态成员变量特性】

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
  2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
  3. 静态成员变量存储在方法区当中
  4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)

2.static修饰成员方法

一般设置的静态成员变量也会被封装设置为private权限,而此时需要对外提供公开接口也就是public权限的静态成员方法,
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。

定义静态成员变量:
[权限修饰符] static 返回值 方法名(形参列表…){方法体}

public static void func(){
//...
}

这也就是刚开始学习方法时写的静态方法,学习static后,方法可以分为静态方法非静态方法

静态方法的访问:

Student.study(); //类名 访问类方法   最推荐这种
student1.study();  // 引用变量访问类方法 不推荐
 ((Student)null).study();  //将null强转为对应类类型 访问类方法 只要是类类型的就可以访问此类里的成员方法或成员变量 不推荐
new Student("张三",18).study();  //可以直接实例化对象得到对象地址后直接调用 类方法或者变量 因为对象地址也属于类类型
Note.test();  // 可以通过当前类名访问其类成员方法
 test();   // 是在当前类里面调用里面的静态方法 可以直接写方法名调用不用加类名 在调用其他类里的静态方法需要在前面加上类名

【静态方法特性】

  1. 不属于某个具体的对象,是类方法
  2. 可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者
  3. 不能在静态方法中访问任何非静态成员变量

3.静态成员变量初始化的方法

静态成员初始化可以通过
1.就地初始化(在定义时右边同时给其赋值)
2.通过静态代码块初始化(涉及到下面的代码块学习)
3.通过调用静态成员方法初始化(set get静态方法)
4.通过构造方法(需要实例化对象后才能进行初始化 不推荐,多此一举)
5.通过非静态成员方法初始化(需要实例化对象又要调用对应的成员方法 不推荐.)

注意:

静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性

4.静态与非静态的简单对比总结

在接触学习static后,在类里的定义就有了静态和非静态之分,它们之间有这很大的不同

①.静态成员变量和非静态成员变量

静态成员变量前面有static修饰 表示类成员,其不属于对象,在类加载时就会在方法区被创建一份空间
不管实例化多少个对象,静态成员变量开辟的空间只有一个(所有对象共享一个)

非静态成员变量前面没有static 是对象成员,属于对象,在实例化对象后会为其在堆区申请开辟一份空间
每实例化一个对象 就会多一份对应的成员变量空间(每个对象都有一份)

静态成员变量通过类访问
非静态成员变量通过对象引用访问

当我们使用一个类时,其一个属性是所有对象共享时,不需要每new一个对象都给其一个空间时就设置静态成员变量,这样设计也能节省空间的开辟.
如: 假设一个班级里的学生,每个学生都是属于这个班级,那么学生类里 班级属性就可以设置为静态的,这样每个学生对象都共用那一个静态成员变量,不用额外再为自己开一个记录班级属性的变量

②.静态成员方法与非静态成员方法

在学习static后,除构造方法外,一个类里可以有静态方法也有非静态方法,其存放在方法区.

1.静态成员方法一般用于给静态成员变量初始化,

为了体现封装性,静态成员变量会被设置为private,此时可以通过静态成员方法作为外部接口给静态成员变量初始化赋值

2.在静态成员方法内不能直接访问非静态成员变量或者成员方法

因为在静态成员方法是可以直接被类名. 调用的,而此时并没有实例化对象 或者可能实例化了多个对象,而此时编译器根本不知道对应的是哪个对象的成员变量和成员方法
也就是静态成员方法内不会存在 this引用,即根本无法对对象进行操作,除非手动传对象地址或者在内部实例化对象操作才能对对象进行访问

3.非静态成员方法内可以访问静态成员方法或者静态成员变量,也可以访问非静态成员变量和非静态成员方法

因为非静态成员方法如果被调用,说明一定实例化了对象,此时可以在成员方法内访问成员变量和其他成员方法,也可以直接访问类成员变量和成员方法

虽然非静态成员方法在方法区也只有一份,但是其内部隐藏了this引用,即不管有多少个对象,每个对象调用这个非静态方法,该方法在执行时会有一个this引用存放当前对象的地址,以便区分不同的对象,对不同对象内的成员进行操作

4.静态成员方法 通过类来访问

在加载时此方法就会存到方法区,可以直接类名访问,如果在当前类里访问可以直接用方法名即可访问

5.非静态成员方法 需要实例化对象后,通过对象引用访问

非静态成员方法被不同对象引用调用其内部this引用可以表示不同对象的地址,以此来表示不同的对象行为,因此可以对指定对象成员进行操作

非静态成员成员方法比静态成员方法内部多了this引用,当需要访问不同对象内的成员时,可以设置为非静态成员方法 表示对象行为 可以更方便调用类里的静态或非静态成员
而静态不具备this引用,不能直接调用非静态的,
还需要在方法内实例化对象或者用形参接受对象引用,
静态成员方法一般用于给静态成员变量初始化,也可以用于一般做算法,刷题等不需要依赖对象使用,此时可以快速调用方法不需要实例化等多余操作

四.Java中的代码块

使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:

普通代码块 构造块 静态块 同步代码块(涉及多线程)

1.普通代码块

普通代码块为定义在方法内部的代码块

public class Main{
public static void main(String[] args) {
//int x;  此处编译错误 在普通代码块内有 x  在其外部上方不能存在同名的变量
{ //直接使用{}定义,普通方法块  不受任何if等限制的 一步步往下执行
int x = 10 ;  //在普通代码块里的 x为10
System.out.println("x1 = " +x);
} 
//结束普通代码块后 x生命周期 作用域都结束!
int x = 100 ;  //在普通代码块外的x 为100
System.out.println("x2 = " +x);
 }
} // 执行结果
x1 = 10
x2 = 100

该代码块用于表示方法内一段局部空间里 进行某些运算.在出这段代码块局部空间后,代码块里面的局部变量作用域生命周期都会结束,下面非普通代码块的区域能用代码块内同名的变量且不会受影响
但是在普通代码块上方不能定义和普通代码块内相同名字的变量

2.构造代码块

构造代码块又称非静态代码块或者实例化代码块:定义在类内部,方法外部


class Person{
  int age =1;  //成员变量 就地初始化赋值为1

   { //定义的类内部 方法外部 
        int age; // 代码块内定义的变量为局部变量 可以和全局变量同名
        this.age=2; // 通过this 引用 访问成员

        System.out.println("非静态代码块/实例化代码块/构造代码块");
        //定义在类里面方法外面的代码块  和构造方法一样一般 用来初始化成员变量的
        //在顺序上和就地初始化成员变量一样 会在编译时放在构造方法前面 具体谁在前面看定义代码所处的先后顺序
        System.out.println(this.age); // 此时就地初始化在前 构造代码块在后
        //构造方法块内的变量是局部变量
 }
}

构造代码块在创建对象的时候执行一次,每创建一个对象都会为其对应的对象执行一次构造代码块
构造代码块一般用于给成员变量进行初始化
在构造代码块内定义的变量是局部变量且可以出现和成员变量同名的局部变量,需要通过this引用访问成员变量
定义的成员变量可以放在构造代码块的前后,如果定义的成员变量有就地初始化语句时 会根据二者定义的位置按先后顺序执行,但是都会在构造方法前执行

3.静态代码块

静态代码块:定义在类内部,方法外部,被static关键字修饰的代码块


class Person{
 static school2="1";
 static{

        school2="6";
        System.out.println(school2);
        System.out.println("静态代码块");//一般用于给静态成员变量初始化

        //类里面方法外面且被static修饰的代码块 在类加载的时候会执行的代码块,只执行一次 有多个代码块时按先后顺序执行
        //当声明静态成员变量在静态代码块后面时 静态代码块内部可以对静态成员变量进行赋值初始化但是不能打印访问!
        // 在静态代码当静态代码块里有对静态成员变量n的打印访问 此时声明静态成员变量n的语句必须在该静态代码块前面 否则会发生非法向前引用
        //静态代码块中的变量是局部变量!!

    }
    //static student2="4"  定义在静态代码块下面 在代码块内打印会发生非法向前引用
}

静态代码块只在类被加载的时候执行一次,实例化对象并不会执行静态代码块,即整个类生命周期只被执行一次
一般用于初始化静态成员变量
静态代码块不能初始化非静态成员变量因为其加载的时候并没有对象(内部不存在this),但是构造代码块可以给静态成员变量赋值(内可以使用this)
当要初始化的静态成员变量有就地初始化时根据其和静态代码块的先后顺序决定哪个先执行

当要初始化的静态成员变量定义在静态成员变量下方时,此时可以给其初始化,但是如果要打印访问时会弹出非法访问错误!

4.分析初始化代码的运行顺序

以下是使用分别有静态和非静态成员变量和对应的就地初始化 以及静态非静态代码块 和构造方法 ,当实例化一个成员对象后,分析一下按什么顺序执行初始化语句

class Student{

    private int age=1;  //1

      //static修饰 成员变量为静态成员变量(类变量)存放在方法区 是加载类时候就申请了空间 不属于对象 是属于类的 也就是所有通过类实例化对象共用的一个变量
    private static String school2="1";//2

   public Student(){ //3
        System.out.println("无参构造方法");

    }

    {

        int age;
        this.age=2;

        System.out.println("非静态代码块/实例化代码块/构造代码块"); //4

        System.out.println(this.age); // 此时就地初始化在前 构造代码块在后

    }
    static{ // 就地初始化在前 代码块在后

        school2="6";
        System.out.println(school2);
        System.out.println("静态代码块");//5


    }


}
public class Note {
public static void main(String[] args) {
        Student student=new Student();// 实例化Student对象后初始化的代码执行顺序是什么?
    }
}

初始化执行顺序为: 2 5 1 4 3

下面简单分析执行顺序:

在实例化Student对象前先加载Student类 ,加载类时 先为静态成员变量开辟空间,然后根据就地初始化和静态代码块先后顺序谁在前先执行谁
再实例化Student对象,给成员变量开辟空间,再按就地初始化和构造代码块先后顺序执行,最后再执行构造方法!

有关Java中static关键字和代码块的学习的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  4. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  5. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  6. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  7. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  10. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

随机推荐