栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Rookie初学Java】关于JVM与一些方法

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Rookie初学Java】关于JVM与一些方法

【Rookie初学Java】 前言:

本篇博客来自作者自己学习的总结和查询,如果存在错误和不足欢迎大家的指出,谢谢。

1 变量,内存

变量分类

成员变量:声明在类中的变量,也是成员属性

局部变量:定义在方法内部,也可以作为形参,或者代码块中的变量

作用域(生存期)

关于变量的作用域或者说是生存期,在经过测试,在Java中,每一对“{}”内就是一个作用域

public class Test02 {
    public static void main(String[] args) {
        int x = 12;
        {
            int y=96;
            System.out.println("x is"+x);
            System.out.println("y is "+y);
        }
        y=x;
        System.out.println("x is "+x);
    }
}
在这个程序中,我们将y定义在{}中,那么对于y来说,一旦出了这个{}意味着它将被释放,结束掉它的生存期
编译通不过,报以下错误
 
    

static修饰的成员包括方法都是静态的,在类加载的时候会被放在方法区中,可以先于对象而产生,这也是为什么对于静态方法我们不需要对象而可以直接调用的原因

关于变量定义的过程,在内存中都发生了什么呢?

找一段代码我们来研究一下

public class Person
{
    private int status;
    private double income;
    private double tax;
    
}
public class Test {
        public static void main(String[] args)
        {
            Scanner s = new Scanner(System.in);
            System.out.println("继续输入纳税人请输入1");
            while(s.nextInt()==1) {
                Person p = new Person();
                System.out.println("请输入纳税人类型:");
                int index = s.nextInt();
                p.setStatus(index);
                System.out.println("请输入收入:");
                double in = s.nextDouble();
                p.setIncome(in);
                System.out.println("纳税人需要缴纳的税额为" + p.money() + "¥");
                System.out.println("继续输入纳税人请输入1");
            }

        }
}

内存一共被分为三块:栈,堆和方法区

    在类加载的时候,class文件的代码片段会被加载到方法区中,就是将代码加载进去

    然后main方法进栈(如果没有static{}代码块的时候)

    代码块顺序执行

    当遇到new 构造方法()的时候将进行如下操作:

      在堆内存中分配空间(new负责做这件事)

      初始化附值

      填充属性

      设置“this值”,即内存地址的值

例如(方法区中还存在static代码块)

其中关于变量名,他只是地址的一个引用,就像我们在c语言中用到的指针,它指向堆内存空间中的一块内存

如果我们把x定义为static变量呢?

public class Test02 {
	static int x ;
    public static void main(String[] args) {
        int y = 9;
        System.out.println("x is "+x);
        System.out.println("y is "+y);
        y=x;
        System.out.println("x is "+x);
    }
}
运行结果如下
    
    

其实对于static修饰的变量和方法,在类加载的时候会被放入方法区中,这也是为什么我们无法在static方法中使用对象的属性,正是因为它先于对象产生,是类的属性和方法

之后对于他们的调用,我们可以直接在代码块中使用,并且可以直接用类名.方法名来调用,可以不用创建出对象,是不是很熟悉呢?没错,在System类中也有一个静态变量我们很熟悉那就是System.out.println(),而out就是System里面的一个静态数据成员,而且这个成员是java.io.PrintStream类的引用。被关键字static修饰的成员可以直接通过"类名.成员名"来引用,而无需创建类的实例。所以System.out是调用了System类的静态数据成员out。

构造方法

构造方法的方法名就是类名,,其实java的实例构造器只负责初始化,不负责创建对象,创建对象是由new指令完成的,这一点有点像c语言中的malloc,构造方法,或者说所有方法中都隐含了一个参数,这个参数是参数列表的第一个参数“this”,静态方法中不能使用this,而构造器中是可以用的,相当于指针一样。因此可以推测,构造方法不是静态方法,更像是类中的另一个特殊的组成部分。值得一提的是,如果自己写了构造方法,那么系统提供的默认的构造方法也就不能用了。

2 哈希值与toString方法

哈希值

public class Test02 {
	
    public static void main(String[] args) {
		
		System.out.println(args);
    }
}

打印出来的结果怎么会这样呢?

如果我们没有写toString方法,将其转化为String类型,那么将会打印出变量的哈希值,哈希值是JVM虚拟出来的地址,并不是真实的物理内存地址。哈希值是逻辑上的唯一性,而内存是物理上的唯一性。

hashcode()

public class Test02 {
    public static void main(String[] args) {
        System.out.println(args.hashCode());
    }
}


public class ComAddr{
    public static void main(String[] args) throws Exception {
        String s1 = "nihao";
        String s2 = "nihao";
        String s3 = new String("nihao");
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        System.out.println(s3.hashCode());
    }
}

由上面三个例子我们是否可以猜测:哈希值是jvm根据物理地址生成的一段特有的,唯一的值;而hashcode()会根据对象的各个字段不同生成一个整数,就像数学函数中的一一对应一样,不同的值会生成不同的整数?

equals()

equals是根类Obeject中的方法。

public boolean equals(Object obj) {
    return (this == obj);
}

可见默认的equals方法,直接调用==,比较对象地址。String类重写了equals(),判断字符串是否相等。String类中的equals首先比较地址,如果是同一个对象的引用,可知对象相等,返回true。如果不是同一个对象,equals方法挨个比较两个字符串对象内的字符,只有完全相等才返回true,否则返回false。

“==”

“==”对于基本数据类型的判断,就是比较他们的值

对于引用数据类型“==”比较的是他们的内存地址

String s1="abc";
String s2="abc";
String s3=new String("abc");
System.out.println("s1和s2 引用地址是否相同:"+(s1 == s2));        //true
System.out.println("s1和s2 值是否相同:"+s1.equals(s2));      //true
System.out.println("s1和s3 引用地址是否相同:"+(s1 == s3));     //false
System.out.println("s1和s3 值是否相同:"+s1.equals(s3));      //true

对于这一段代码,s1==s2为true,我有一个猜想,不到一定对,关于方法区中的常量是否也有一个地址?

结论

关于哈希值的算法是否存在很多种?当我们调用hashcode()方法的时候得到的哈希值和直接打印出来的哈希值不一样,是不是说明了哈希值存在很多种计算方法。

关于比较元素的时候,其实是先比较hashcode是否相等,如果相等,那么将会继续使用equals方法对每一个区域进行比较,而“==”对于引用数据类型则会直接比较他们的内存地址。

toString()

就像字面上的意思,toString()方法就是告诉jvm返回该对象的字符串表示,如果在该类中我们没有写toString(),那么我们在使用对象的引用的时候会返回一个哈希值,反之,则会根据toString()返回相应String类型,就像是文本一样。

public class Test02 {
	
    public static void main(String[] args) {
		Student stu = new Student();
		System.out.println(stu);
    }
}

如果重写了toString()

public class Student 
{
	int id;
	String name;
	int age;
	@Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", age=" + age +
               ''' +
                '}';
    }
}

public class Test02 {
	
    public static void main(String[] args) {
		Student stu = new Student();
		System.out.println(stu);
    }
}

3 未证明的问题
    哈希值存在多种算法(查询后发现应该是这样)方法区常量池中的常量是否也有地址?(度娘和博客没有相关答案,但是根据“==”我们有理由猜测方法区中的常量是有地址的)
后记:
    关于JVM内存图不仅仅是被简单的分为三部分,其中涉及的内容非常丰富,我还将继续深入学习并作总结toString更像是一个规范,让JVM知道变量应该怎么来称呼,就像名字一样方法区中的常量池确实是有地址的,已经的以证明。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/763040.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号