【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来说,一旦出了这个{}意味着它将被释放,结束掉它的生存期
    编译通不过,报以下错误
     /*
     Test02.java:9: 错误: 找不到符号
            y=x;
            ^
      符号:   变量 y
      位置: 类 Test02
    1 个错误
    */
        
    
  • static修饰的成员包括方法都是静态的,在类加载的时候会被放在方法区中,可以先于对象而产生,这也是为什么对于静态方法我们不需要对象而可以直接调用的原因

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

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

    public class Person
    {
        private int status;
        private double income;
        private double tax;
        /*
        get...set....
        */
    }
    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");
                }
    
            }
    }
    

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

请添加图片描述

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

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

  3. 代码块顺序执行

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

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

    2. 初始化附值

    3. 填充属性

    4. 设置“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);
    }
}
运行结果如下
    
    /*
    x is 0
	y is 9
	x is 0
	*/

其实对于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);
        }
    }
    /*
    
    D:\EditPlus\Test>java Test02
    [Ljava.lang.String;@15db9742
    
    */
    

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

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

  • hashcode()

    public class Test02 {
        public static void main(String[] args) {
            System.out.println(args.hashCode());
        }
    }
    /*
    1836019240
    */
    
    
    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());
        }
    }
    /*
    输出结果:
    104818427
    104818427
    104818427
    */
    

由上面三个例子我们是否可以猜测:哈希值是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);
    }
}
/*
D:\EditPlus\Test>java Test02
Student@15db9742
*/

如果重写了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);
    }
}
/*
D:\EditPlus\Test>java Test02
Student{id=0, name='null', age=0'}
*/

3 未证明的问题

  1. 哈希值存在多种算法(查询后发现应该是这样)
  2. 方法区常量池中的常量是否也有地址?(度娘和博客没有相关答案,但是根据“==”我们有理由猜测方法区中的常量是有地址的)

后记:

  1. 关于JVM内存图不仅仅是被简单的分为三部分,其中涉及的内容非常丰富,我还将继续深入学习并作总结
  2. toString更像是一个规范,让JVM知道变量应该怎么来称呼,就像名字一样
  3. 方法区中的常量池确实是有地址的,已经的以证明。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值