Java中static详解和JVM内存分配

简介: Java中static详解和JVM内存分配


目录

变量分类

静态变量与实例变量

静态方法和实例方法

静态代码块与实例代码块

JVM的内存结构


变量分类

局部变量:在方法体里面定义的变量,该变量会在程序执行到方法体时被初始化,存储在栈( stack)内存中。

成员变量:类体之内,方法体之外定义的变量,它又分为实例变量和静态变量

实例变量:没有被static修饰的成员变量,实例变量是对象所拥有的,在创建对象时被初始化,存 储在堆内存中。

静态变量:被static修饰的成员变量,静态变量是被所有对象共享的,在类加载时会被初始化,存储在方法区中。

静态变量与实例变量

上面对Java中的变量进行了简单概述,下面我们详细讲解一下静态变量与实例变量

代码1:

//定义了一个people类
class people
{
  //实例变量
  //不能直接访问,需要new对象之后再访问
  String name;
  int age;
}
public class Test01
{
  public static void main(String[] args){
    //创建了people类的对象
  people s1 = new people();
  s1.name = " 张三 ";
  s1.age = 18;
  System.out.println("姓名->" + s1.name + "年龄->" + s1.age);
  people s2 = new people();
  s2.name = " 李四 ";
  s2.age = 19;
  System.out.println("姓名->" + s2.name + "年龄->" + s2.age);
  }
}

运行结果:

总结如下:

(1)实例变量的访问必须首先创建对象,然后通过 " 引用 . "的方式访问。

(2)实例变量如果直接通过" 类名. "的方式访问,会报以下错误。

(3)实例变量是每个对象独自拥有的,存在多个副本,每个副本互不影响。(s1,s2都有自己对应的名字和年龄,二者互不影响)。

代码2:

class people
{
  //静态变量
  static String country = "中国";
}
public class Test01
{
  public static void main(String[] args){
    System.out.println(people.country);
    people s1 = new people();
    people s2 = new people();
    System.out.println("s1->" + s1.country + " s2->" + s2.country);
    //更改s1对应的country
    s1.country = "中华";
    System.out.println("s1->" + s1.country + " s2->" + s2.country);
  }
}

运行结果:

 

总结如下:

(1)静态变量既可以通过" 引用 . "的方式访问,也可以通过" 类名. "的方式访问(此方法不需要对象)。

(2)静态变量是所有对象所共享的,在内存中只有一个副本(通过s1访问country和通过s2访问country其实访问的是同一个)。

(3)其实程序在运行时,遇见用引用访问静态变量,系统会将引用自动看成类名。

(4)在写代码时,访问静态变量最好还是使用" 类名. "的方式访问,以免其他人在读代码时造成不必要的误区。

那么什么时候用实例变量,什么时候用静态变量呢?

简单的说就是,如果一个变量所描述的属性是每一个对象都不一样的,那么就用实例变量,如果这个属性是属于这个类的整体特征,每一个对象都一样,那么就使用静态变量。

如果大家还有疑惑,想必看完下面代码就会清楚。

//中国人的类
class Chinese{
  //姓名,每个人都有自己的姓名
  String name;
  //身份证号,每个人都有自己的身份证号
  String idCard;
  //国籍,中国人的国籍都是中国,这是属于类的整体特征
  //如果声明为实例变量,每个对象都一样,就会浪费空间
  static String country = "中国";
  public Chinese(String x1 , String x2){
    name = x1;
    idCard = x2;
  }
}

静态方法和实例方法

学会了静态变量与实例变量的区别,再来学习静态方法与实例方法就会很简单。

静态方法与实例方法的语法结构有什么不同呢?

静态方法:修饰符+static  返回值类型   方法名(形式参数列表)

实例方法:修饰符(不含static)  返回值类型   方法名(形式参数列表)

静态方法与实例方法有着静态变量与实例变量相同的区别

代码3:

class test
{
  //静态方法A
  public static void A(){
    System.out.println("A");
  }
  //实例变量B
  public void B(){
    System.out.println("B");
  }
}
public class Test01
{
  public static void main(String[] args){
    //通过"类名."的方式访问静态方法A(不需要对象)
    test.A();
    //创建test类的对象
    test s = new test();
    //通过"引用."的方式访问静态方法A
    s.A();
    //通过"引用."的方式访问静态方法B
    s.B();
  }
}

运行结果:

总结如下:

(1)实例方法只能通过" 引用 . "的方式访问,静态方法既可以通过" 引用 . "的方式访问,也可以通过" 类名. "的方式访问(此方法不需要对象)。

(2)实例方法是每个对象独自拥有的,存在多个副本,每个副本互不影响,静态方法是所有对象所共享的,在内存中只有一个副本。

(3)其实程序在运行时,遇见用引用访问静态方法,系统会将引用自动看成类名。

上面都照搬静态变量与实例变量之间的区别,除此之外还有什么是需要注意的呢?

(4)静态方法中不能访问实例变量,若访问了,会报以下错误:

报的错误虽然和使用" 类名. " 的方式访问实例变量一样,但是要清楚,这是两个不同的错误。

(5)无论是静态方法还是实例方法,甚至是构造方法,在运行时都需要压栈。

那么什么时候用实例方法,什么时候用静态方法呢?

我总结出了以下两点:

(1)当该方法体当中访问了实例变量,该方法必须是实例方法,否则会报错。

(2)若不同对象进行这个"行为"(方法)时结果不同,那么需要使用实例方法,反之则用静态方法。

静态代码块与实例代码块

这块儿内容十分简单,我们先看看二者的语法结构

//静态代码块
static{
    java语句;
    java语句;
}
//实例代码块
{
    java语句;
    java语句;
}

静态代码块就是一个static加一个花括号,括号里面写Java语句。

实例代码块直接就是一个花括号,括号里面写Java语句。

我们先来说一下他们的特点:

(1)静态代码块在类加载时执行,且只执行一次(在main方法之前执行)。

(2)实例代码块并未在类加载时执行,只要是构造方法执行(创建对象时),一定会在构造方法执行之前执行实例代码块。

(3)静态代码块和实例代码块要放在类体之内,方法体之外。

下面我们分别来感受一下两种代码块:

静态代码块

代码4:

public class Test01
{
  public static void A(){
    System.out.println("方法A开始执行!");
  }
  //静态代码块
  static{
    System.out.println("静态代码块1开始执行!");
  }
  public static void main(String[] args){
    A();
  }
  static{
    System.out.println("静态代码块2开始执行!");
  }
}

运行结果:

实例代码块

代码5

class test
{
  //实例代码块1
  {
    System.out.println("实例代码块1执行!");
  }
  //构造方法1
  public test(){
    System.out.println("构造方法执行!");
  }
  //实例代码块2
  {
    System.out.println("实例代码块2执行!");
  }
}
public class Test01
{
  public static void main(String[] args){
    test s1 = new test();
    test s2 = new test();
  }
}

运行结果:

JVM的内存结构

我们学习了上面内容后,再了解一下它们是如何在内存中存在的

代码6

class Chinese{
  //姓名,每个人都有自己的姓名
  String name;
  //身份证号,每个人都有自己的身份证号
  String idCard;
  //国籍,中国人的国籍都是中国,这是属于类的整体特征
  //如果声明为实例变量,每个对象都一样,就会浪费空间
  static String country = "中国";
  public Chinese(String x1 , String x2){
    name = x1;
    idCard = x2;
  }
}
public class Test
{
  public static void main(String[] args)
  { 
    System.out.println(Chinese.country);
    Chinese people1 = new Chinese("张三" , "610234434344243");
    System.out.println(people1.name);
    System.out.println(people1.idCard);
    Chinese people2 = new Chinese("李四" , "610238838238237");
    System.out.println(people2.name);
    System.out.println(people2.idCard);
  }
}

下面是以上代码在JVM中的内存分配 :


相关文章
|
15天前
|
算法 安全 Java
Java内存管理:深入理解垃圾收集器
在Java的世界里,内存管理是一块基石,它支撑着应用程序的稳定运行。本文将带你走进Java的垃圾收集器(GC),探索它是如何默默守护着我们的内存安全。我们将从垃圾收集的基本概念出发,逐步深入到不同垃圾收集器的工作机制,并通过实例分析它们在实际应用中的表现。文章不仅旨在提升你对Java内存管理的认识,更希望你能通过这些知识优化你的代码,让程序运行更加高效。
34 3
|
6天前
|
存储 缓存 监控
【Java面试题汇总】JVM篇(2023版)
JVM内存模型、双亲委派模型、类加载机制、内存溢出、垃圾回收机制、内存泄漏、垃圾回收流程、垃圾回收器、G1、CMS、JVM调优
【Java面试题汇总】JVM篇(2023版)
|
5天前
|
监控 算法 Java
Java中的内存管理:理解垃圾回收机制的深度剖析
在Java编程语言中,内存管理是一个核心概念。本文将深入探讨Java的垃圾回收(GC)机制,解析其工作原理、重要性以及优化方法。通过本文,您不仅会了解到基础的GC知识,还将掌握如何在实际开发中高效利用这一机制。
|
5天前
|
存储 监控 算法
Java中的内存管理与垃圾回收机制解析
本文深入探讨了Java编程语言中的内存管理策略和垃圾回收机制。首先介绍了Java内存模型的基本概念,包括堆、栈以及方法区的划分和各自的功能。进一步详细阐述了垃圾回收的基本原理、常见算法(如标记-清除、复制、标记-整理等),以及如何通过JVM参数调优垃圾回收器的性能。此外,还讨论了Java 9引入的接口变化对垃圾回收的影响,以及如何通过Shenandoah等现代垃圾回收器提升应用性能。最后,提供了一些编写高效Java代码的实践建议,帮助开发者更好地理解和管理Java应用的内存使用。
|
13天前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
43 11
|
14天前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
38 11
|
11天前
|
监控 Java 大数据
【Java内存管理新突破】JDK 22:细粒度内存管理API,精准控制每一块内存!
【9月更文挑战第9天】虽然目前JDK 22的确切内容尚未公布,但我们可以根据Java语言的发展趋势和社区的需求,预测细粒度内存管理API可能成为未来Java内存管理领域的新突破。这套API将为开发者提供前所未有的内存控制能力,助力Java应用在更多领域发挥更大作用。我们期待JDK 22的发布,期待Java语言在内存管理领域的持续创新和发展。
|
7天前
|
存储 缓存 算法
Java中的内存管理:理解垃圾回收机制
本文将深入探讨Java中的内存管理,特别是垃圾回收机制。我们将从基本的内存分配开始,逐步解析垃圾回收的原理和过程,以及它对Java应用程序性能的影响。通过实例演示,我们会展示如何在Java中有效地管理和优化内存使用。最后,我们将讨论一些常见的内存泄漏问题及其解决方案。
|
17天前
|
安全 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法的深度融合
在Java的演进历程中,Lambda表达式无疑是Java 8引入的一项革命性特性,它极大地简化了函数式编程在Java中的应用,使得代码更加简洁、易于阅读和维护。而这一切的背后,JVM的invokedynamic指令功不可没。本文将深入探讨invokedynamic指令的工作原理及其与Java Lambda语法的紧密联系,带您领略这一技术背后的奥秘。
13 1
|
1天前
|
监控 算法 Java
Java中的内存管理:理解Garbage Collection机制
本文将深入探讨Java编程语言中的内存管理,特别是垃圾回收(Garbage Collection, GC)机制。我们将从基础概念开始,逐步解析垃圾回收的工作原理、不同类型的垃圾回收器以及它们在实际项目中的应用。通过实际案例,读者将能更好地理解Java应用的性能调优技巧及最佳实践。
9 0