精华内容
下载资源
问答
  • java对象存储在哪里

    千次阅读 2020-02-29 14:29:17
    1、寄存器 寄存器是速度最快的存储区域,...常用于存放对象引用与基本数据类型,不存放Java对象。栈内存被要求存放在其中的数据的大小、生命周期必须是已经确定的。 3、堆 通用的内存池,位于RAM中,用于存放所有的...

    1、寄存器
    寄存器是速度最快的存储区域,它位于处理器内部,但它的数量有限,所以要按需分配,不能被人控制。
    2、堆栈
    通常也叫栈,位于RAM中,堆栈指针向下移动,则分配新的内存;向上移动,则释放那些内存。这种存储方式速度仅次于寄存器。常用于存放对象引用与基本数据类型,不存放Java对象。栈内存被要求存放在其中的数据的大小、生命周期必须是已经确定的。
    3、堆
    通用的内存池,位于RAM中,用于存放所有的Java对象。编译器不需要知道存储的数据在堆中存活多长时间;当需要一个对象时,用new写一行代码,当执行这行代码时,会自动在堆中进行存储分配,同时,有以上原因,用堆进行存储分配和清理可能比堆栈进行存储分配花更多的时间。
    4、常量存储
    常量值通常直接存放在程序代码内部,这样做是安全的,因为它们永远不会改变。但有时在嵌入式系统中,常量本身会和其他部分隔开,放在ROM中。
    5、非RAM存储
    如果数据完全存活在程序之外,那么它可以不受程序控制,程序没有运行时也可以存在。比如流对象与持久化对象。

    特殊:基本类型
    在Java中,new创建的对象都存放在堆中,但用new创建一个特别小的,简单的变量,就比较麻烦,所以Java采用了与c一样的方法,不用new来创建变量,而是创建一个不是引用的变量,这个变量将值存储在栈中,而且这些变量占的内存空间是确定的,不随机器硬件架构而变化。
    Java的数组
    在C/C++语言中,数组就是个内存块,超出内存块访问数据或在内存初始化前访问数据是很危险的;
    Java会确保数组被初始化,而且不能在它的范围外访问,这种范围检查,以每个数组上少量的内存开销和运行时的下标检查为代价。
    当创建一个Java数组时,世纪上创建了一个引用数组,每个引用都会初始化为null。
    但创建一个用来存放基本数据类型的数组时,Java也会把它初始化,就算将数组所占的内存全部置为0。


    在Java中,栈里面的数据是可以共享的
    比如我们定义一个变量:

    int a = 1;
    int b = 1;

    编译器就会先在栈中开辟一块空间存储引用a,然后在栈中查看是否有1这个值存在,发现没有,则在栈中开辟空间存储1这个值;然后再开辟空间存储b,在栈中查看是否有1,发现存在1这个值,那就把b指向1的地址。
    用这样数据共享的方式可以节省空间,防止反复的向内存加入同样的值。


    Java的String是比较特殊的。
    比如这行代码

    String s = “123”;
    String b = “123”;
    System.out.print(s == b);

    这段代码先在字符串常量池中查找是否有”123“这个字符串(关于字符串常量池可以看这里),没有的话,则在字符串常量池中创建。很明显,b字符串创建时常量池里已经有123这个字符串了,把它指向123的地址就好了,所以打印出true。

    String sc = new String(“123”);
    String s = “123”;

    这两行代码的区别是:
    第一行代码的步骤是
    1、先定义一个名为sc的引用
    2、然后检索常量池中是否有123的字符串
    3、如果不存在,则在字符串常量池中存储进一个值为"abc"的字符串对象。
    4、在堆中创建存储一个”abc“字符串对象
    5、将对象引用指向堆中的对象
    第二行代码和第一行代码的前三步都一样,但后面则是直接将s指向字符串常量池当中的”123“对象。
    这里指的注意的是,采用new的方式,虽然是在堆中存储对象,但是也会在存储之前检查常量池中是否已经含有此对象,如果没有,则会先在常量池创建对象,然后在堆中创建这个对象的”拷贝对象“。这也就是为什么有道面试题:String s = new String(“xyz”);产生几个对象?的答案是:一个或两个的原因。因为如果常量池中原来没有”xyz”,就是两个。


    最后再转载一个网上看到的一个例子,帮助对栈内存、堆内存的存储进行理解:

    class BirthDate {
        private int day;
        private int month;
        private int year;
        public BirthDate(int d, int m, int y) {
            day = d;
            month = m;
            year = y;
        }
        省略get,set方法………
    }
    
    public class Test {
        public static void main(String args[]) {
            int date = 9;
            Test test = new Test();
            test.change(date);
            BirthDate d1 = new BirthDate(7, 7, 1970);
        }
    
        public void change1(int i) {
            i = 1234;
        }
    }
    

    对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:

    1. main方法开始执行:int date = 9;
      date局部变量,基础类型,引用和值都存在栈中。
    2. Test test = new Test();
      test为对象引用,存在栈中,对象(new Test())存在堆中。
    3. test.change(date);
      调用change(int i)方法,i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。
    4. BirthDate d1= new BirthDate(7,7,1970);
      调用BIrthDate类的构造函数生成对象。
      d1为对象引用,存在栈中;
      对象(new BirthDate())存在堆中;
      其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中;
      day,month,year为BirthDate对象的的成员变量,它们存储在堆中存储的new BirthDate()对象里面;
      当BirthDate构造方法执行完之后,d,m,y将从栈中消失。
    5. main方法执行完之后。
      date变量,test,d1引用将从栈中消失;
      new Test(),new BirthDate()将等待垃圾回收器进行回收。
    展开全文
  • 什么是Java的对象引用? Java中都有哪些类型的对象引用? Java中提供的Java对象引用主要有什么目的? 通过本文,你就能很清楚得了解Java的对象引用

    Java对象的引用

    一、概念,什么是Java对象的引用?
      每种编程语言都有自己的数据处理方式。有些时候,程序员必须注意将要处理的数据是什么类型。你是直接操纵元素,还是用某种基于特殊语法的间接表示(例如C/C++里的指针)来操作对象。所有这些在 Java 里都得到了简化,一切都被视为对象。因此,我们可采用一种统一的语法。尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“引用”(reference)。 对Java对象的引用,是描述的定义。 
      
    二、Java对象引用的目的
    Java中提供这四种引用类型主要有两个目的:
    第一是 可以让程序员通过代码的方式决定某些对象的生命周期
    第二是 有利于JVM进行垃圾回收

    三、四中Java对象的引用
    Java对象的引用包括:强引用,软引用,弱引用,虚引用
    强引用:是指创建一个对象并把这个对象赋给一个引用变量。
    软引用:如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;
    如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被 程序使用。软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。
    SoftReference的特点是它的一个实例保存对一个Java对象的软引用, 该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。
    弱引用:WeakReference弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。
    虚引用:虚引用(PhantomReference) 虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收 。

    要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

    展开全文
  • JAVA 面向对象方法详解

    千次阅读 2016-07-17 23:08:29
    方法不能独立存在. 方法在逻辑上要么属于类, 要么属于对象. 方法的所属性 一旦将一个方法定义在某个类的类体内. 如果这个方法使用了static修饰符,则这个方法属于这个类. 否则这个方法属于这个类的实例.

    本页面更新日期: 2016年07月18日

    前言

    方法是类或对象 的行为特征的抽象.
    方法是类或对象最重要的组成部分.
    所有的方法都必需定义在类里.
    方法不能独立存在.
    方法在逻辑上要么属于类, 要么属于对象.

    方法的所属性

    一旦将一个方法定义在某个类的类体内.
    如果这个方法使用了static修饰符,则这个方法属于这个类.
    否则这个方法属于这个类的实例.

    Java语言是静态的.
    一个类定义完成后,只要不再重新编译这个类文件,该类和该类的对象所拥有的方法是固定的,永远都不会改变.

    执行方法时必需使用类或对象来作为调用者.
    即所有方法都必需使用 类.方法 或 对象.方法 的形式来调用.

    这里可能有个问题: 同一个类里不同方法之间相互调用时,
    不就可以直接调用嘛?

    这里需要指出:
    同一个类的一个方法调用另一个方法时,如果被调用方法是普通方法,则默认使用 this 作为调用者;
    如果被调方法是静态方法,则默认使用类作为调用者.
    也就是说,表面上看起来某些方法可以被独立执行, 但实际上还是使用 this 或者 类 来作为调用者.

    永远不要把方法当成独立存在的实体.
    正如现实世界由类和对象组成,而方法只能作为类和对象的附属.
    Java语言里方法的所属性主要体现在如下几个方面:

    • 方法不能独立定义, 方法只能在类体里定义.
    • 从逻辑意义上来看,方法要么属于该类本身,要么属于该类的一个对象.
    • 永远不能独立执行方法,执行方法必需使用类或对象作为调用者.

    使用 static 修饰符的方法属于这个类本身.
    使用 static 修饰付的方法 既可以使用类作为调用者来调用.
    也可以使用对象作为调用者来调用.
    但需要指出的是, 因为使用 static 修饰的方法还是属于这个类的.
    因此使用该类的任何对象来调用这个方法时将会得到相同的执行结果.
    这是由于底层依然是使用这些实例所属的类作为调用者.

    虽然Java允许你写的时候,拿该类的实例来调用 static 修饰的类方法.
    但是你要明白其中的运行机制.
    所以建议你自己写程序的时候, 尽量不要混着用.
    用实例来调用实例的方法, 用类来调用 static 修饰的类方法.

    没有 static 修饰符的方法则属于该类的对象,不属于这个类本身.
    因此没有 static 修饰符的方法只能使用对象作为调用者来调用.
    不能使用类作为调用者来调用.
    使用不同对象作为调用者来调用同一个普通方法可能会得到不同的结果.

    看到这里, 你应该明白, 使用 static 和 不使用 static 修饰的方法是有区别的.
    以后我们写程序的时候, 你慢慢就能体会到其中的奥妙.

    方法的参数传递机制

    如果声明方法时包含了形参声明.
    则调用方法时必需给这些形参指定参数值.
    调用方法时实际传给形参的参数值也被称为实参.

    那么,Java的实参值是如何传入方法的呢?
    这是由Java方法的参数传递机制来控制的.
    Java里方法参数传递方式只有一种 – 值传递.
    所谓值传递,就是将实际参数值的副本(复制品)传入方法内.
    而参数本身不会受到任何影响.

    小插曲:
    Java里参数传递类似于<西游记>里的孙悟空.
    孙悟空复制了一个假孙悟空,这个假孙悟空具有和孙悟空相同的能力.
    可除妖或被砍头.
    但不管这个假孙悟空遇到什么事,真孙悟空不会受到任何影响.
    与此类似,传入方法的是实际参数的复制品.
    不管方法中对这个复制品如何操作,实际参数值本身不会受到任何影响.

    写个程序演示一下方法参数传递的效果:

    public class PrimitiveTransferTest
    {
      public static void swap(int a, int b)
      {
        //下面三行代码实现 a, b变量的值交换
        //定义一个临时变量来保存 a 变量的值
        int tmp = a;
        //把 b 的值赋给 a
        a = b;
        //把临时变量 tmp 的值赋给 a
        b = tmp;
        System.out.println("swap方法里, a 的值是:" + a + "; b 的值是:" + b);
      }
    
      public static void main(String[] args)
      {
        int a = 6;
        int b = 9;
        PrimitiveTransferTest.swap(a, b);
        System.out.println("交换结束后, 变量 a 的值是:" + a + "; 变量 b 的值是:" + b);
      }
    }
    

    运行之后, 你可以结果中得知.
    swap() 方法里的 a 的值是 9 ; b 的值是 6
    交换结束后, 变量 a 的值依然是 6; 变量 b 的值依然是 9
    可以得出以下结论:
    main() 方法里的变量 a 和 b
    并不是 swap() 方法里的 a 和 b
    正如前面所说, swap() 方法里的 a 和 b 只是 main() 方法里的变量 a 和 b 的复制品.
    下面我画个图演示一下其执行过程.

    Java程序总是从 main() 方法开始执行, main()方法开始定义了 a / b 两个局部变量.
    两个变量在内存中的存储示意图如下图所示.

    这里写图片描述

    当程序执行 swap() 方法时, 系统进入 swap()方法, 并将 main() 方法中的 a / b 变量作为参数值传入 swap() 方法.
    传入 swap() 方法的只是 a / b 的副本, 而不是 a / b 本身.
    进入 swap() 方法后系统产生了 4 个变量, 这 4 个变量在内存中的存储示意图如下:

    这里写图片描述

    系统分别为 main() 方法 和 swap() 方法分配了两块栈区.
    用于保存 main() 方法 和 swap() 方法的局部变量.
    main() 方法中的 a / b 变量作为参数值传入 swap() 方法.
    实际上是在 swap() 方法的栈区中 重新产生了两个变量 a / b.
    并将 main() 方法的栈区中 a / b 变量的值分别赋给 swap() 方法的栈区中的 a / b 参数.
    此时, 系统存在两个 a 变量 / 两个 b 变量.
    只是存放在不同方法的不同栈区中.

    程序在 swap() 方法中交换 a / b 两个变量的值.
    交换结束后, 我们看到 a 的值是 9 / b 的值是 6
    此时内存中的存储示意图如下图所示:

    这里写图片描述

    我们可以看到, 在所有示意图中, main() 方法栈区中的 a / b 的值并未有任何改变.
    程序改变的只是 swap() 方法栈区中的 a 和 b.
    这就是值传递的实质:
    当系统开始执行方法时, 系统为形参执行初始化.
    就是把实参变量的值赋给方法的形参变量.
    方法里操作的并不是实际的实参变量.

    上面你看到的是 基本类型的参数传递.
    Java对于引用类型的参数传递, 一样采用的是值传递方式.
    但许多小学生可能对引用类型的参数传递产生误会.
    下面程序示范了引用类型的参数传递效果.

    class DataWrap
    {
      int a;
      int b;
    }
    
    public class ReferenceTransferTest
    {
      public static void swap(DataWrap dw)
      {
        //下面三行代码实现 dw 的 a / b 两个成员变量的值交换
        //定义一个临时变量来保存 dw 对象的 a 成员变量的值
        int tmp = dw.a;
        //把 dw 对象的 b 成员变量的值赋给 a 成员变量
        dw.a = dw.b;
        //把临时变量 tmp 的值赋给 dw 对象的 b 成员变量
        dw.b = tmp;
        //输出看看结果
        System.out.println("swap 方法里, a 成员变量的值是:" + dw.a + "; b 成员变量的值是:" + dw.b);
      }
      public static void main(String[] args)
      {
        DataWrap dw = new DataWrap();
        dw.a = 6;
        dw.b = 9;
        swap(dw);
        System.out.println("交换结束后, a 成员变量的值是:" + dw.a + "; b 成员变量的值是:" + dw.b);
      }
    }
    

    从运行结果来看, 在 swap() 方法里, a / b 两个成员变量的值被交换成功.
    不仅如此, 当 swap() 方法执行结束后, main() 方法里 a / b 两个成员变量的值也被交换了.
    这很容易造成一种错觉:
    调用 swap() 方法时, 传入 swap() 方法的就是 dw 对象本身, 而不是它的复制品.
    但!!! 这仅仅是一种错觉.
    下面还是结合示意图来说明程序的执行过程.

    程序从 main() 方法开始执行, main() 方法开始创建了一个 DataWrap 对象,
    并定义了一个 dw 引用变量来指向 DataWrap 对象, 这是一个与基本类型不同的地方.
    创建一个对象时, 系统内存中由两个东西:
    堆内存中保存了对象本身.
    栈内存中保存了引用该对象的引用变量.
    接着程序通过引用来操作 DataWrap 对象.
    把该对象的 a / b 两个成员变量分别赋值为 6 和 9
    此时系统内存中的存储示意图如下:

    这里写图片描述

    接下来, main() 方法中开始调用 swap() 方法.
    系统会分别为 main() 和 swap() 开辟出两个栈区.
    用于存放 main() 和 swap() 方法的局部变量.
    调用 swap() 方法时, dw 变量作为实参传入 swap() 方法.
    同样采用的是值传递方式:
    把 main() 方法里的 dw 变量的值 赋给 swap() 方法里的 dw 形参.
    从而完成 swap() 方法的 dw 形参的初始化.
    值得注意的是, main() 方法中的 dw 是一个引用.
    它保存了 DataWrap 对象的地址.
    当把 dw 的值赋给 swap() 方法的 dw 形参后.
    即让 swap() 方法的 dw 形参也保存这个地址值.
    即也会引用到堆内存中的DataWrap对象.
    下图演示 dw 传入 swap() 方法后的存储示意图:

    这里写图片描述

    可以由图看出, 这种参数传递方式 是不折不扣 的值传递方式.
    系统一样复制了 dw 的副本传入 swap() 方法.
    但关键在于 dw 只是一个引用变量, 所以系统复制了 dw 变量, 但并未复制 DataWrap 对象.

    当程序在 swap() 方法中操作 dw 形参时, 由于 dw 只是一个引用变量.
    所以实际操作的还是堆内存中的DataWrap对象.
    此时, 不管是操作 main() 方法里的 dw() 变量, 还是操作 swap() 方法里的 dw() 参数.
    其实都是操作它们共同引用的 DataWrap 对象.
    因为它们操作的都是同一个对象
    所以在 swap() 方法中交换 dw 参数所引用的 DataWrap 对象的 a / b 两个成员变量的值后.
    可以看到 main() 方法中 dw 变量所引用的 DataWrap 对象的 a / b 两个成员变量的值也被交换了.

    为了更好的证明 main() 方法中的 dw 和 swap() 方法中的 dw 是两个变量.
    在 swap() 方法的最后一行增加如下代码:

    //把 dw 赋值为 null , 让它不再指向任何有效地址
    dw = null;

    执行上面代码的结果是 swap() 方法中的 dw 变量不再指向任何有效内存.
    程序其它地方不做任何修改.
    main() 方法调用了 swap() 方法后, 再次访问 dw 变量的 a / b 两个成员变量, 依然可以输出 9 和 6.
    可见 main() 方法中的 dw 变量没有受到任何影响.
    实际上, 当 swap() 方法中增加 dw=null 代码后, 内存中的存储示意图如下:

    这里写图片描述

    从上图可以看到, 把 swap() 方法中的 dw 赋值为 null 后.
    swap() 方法中 失去了 DataWrap 的引用, 不可以再访问堆内存中的 DataWrap 对象.
    但 main() 方法中的 dw 变量不受任何影响, 依然引用着 DataWrap 对象.
    所以依然可以输出 DataWrap 对象的 a / b 成员变量的值.

    形参个数可变的方法

    Java允许定义形参个数可变的参数.
    从而允许为方法指定数量不确定的形参.
    如果在定义方法时, 在最后一个形参的类型后增加三个点 …
    则表明该形参可以接受多个参数值.
    多个参数值将被当成数组传入.
    下面我定义一个形参个数可变的方法:

    
    public class Varargs
    {
      //定义了形参个数可变的方法
      public static void test(int a, String... books)
      {
        //boos 被当成数组处理
        for(String tmp : books)
        {
          System.out.println(tmp);
        }
        //输出整数变量 a 的值
        System.out.println(a);
      }
      public static void main(String[] args)
      {
        //调用 test 方法
        test(5, "我是谁?", "我是比克大魔王", "哈哈哈!", "你真逗比!");
      }
    }
    

    从上面程序的运行结果可以看出.
    调用 test() 方法时, books 参数可以传入多个字符串作为参数值.
    从 test() 的方法体代码来看, 形参个数可变的参数本质就是一个数组参数.
    小提示: 下面两个写法效果是一样的.

    //以可变个数形参来定义方法
    public static void test(int a, String... books);
    //采用数组形参来定义方法
    public static void test(int a, String[] books);

    这两种形式都包含了一个名为 books 的数组形参.
    不过在使用它们时有区别, 对于以可变形参的形式定义的方法.
    调用时写法更加简洁.如下面代码所示:

    test(5, "我是谁?", "我是比克大魔王", "哈哈哈!", "你真逗比!");

    但如果采用数组参数来定义方法的话, 就要这样写:

    test(5, new String[]{"我是谁?", "我是比克大魔王", "哈哈哈!", "你真逗比!"});

    对比之后, 明显第一种形式更加简洁.
    实际上, 即使你采用 形参个数可变的形式来定义方法, 调用该方法时也一样可以为其传入一个数组.

    最后需要指出的是:
    数组形式的形参可以处于形参列表的任意位置.
    个数可变的形参只能只能处于形参列表的最后.
    也就是说, 一个方法中最多只能有一个长度可变的形参.
    知道真相的你是不是眼泪掉下来, 看来不是所有情景都适合使用更简洁的 长度可变的形参.

    递归方法

    一个方法体内调用它自身,被称为方法递归.
    方法递归包含了隐式的循环,它会重复执行某段代码,但这种重复执行无需循环控制.

    例如有如下数学题.
    已知有一个数列: (数列是什么鬼?)
    f(0) = 1, f(1) = 4, f(n+2) = 2 * f (n+1) + f(n)
    其中 n 是大于 0 的整数, 求 f(10) 的值.
    这个问题可以用 递归 来求得.
    下面程序将定义一个 fn 方法, 用于计算 f(10) 的值.

    public class Recursive
    {
      public static int fn(int n)
      {
        if(n == 0)
        {
          return 1;
        }
        else if(n == 1)
        {
          return 4;
        }
        else
        {
          //方法中调用它自身,就是方法递归
          return 2 * fn(n - 1) + fn(n - 2);
        }
      }
      public static void main(String[] args)
      {
        //输出 fn(10) 的结果
        System.out.println(fn(10));
      }
    }
    

    对于 fn(10) 而言.
    即等于 2 * fn(9) + fn(8), 其中fn(9)又等于 2 * fn(8) + fn(7) …… 以此类推.
    最终会计算到 fn(2) 等于 2 * fn(1) + fn(0) 即 fn(2) 是可计算的.
    然后一路反算回去, 就可以最终得到 fn(10) 的值.

    仔细看上面递归的过程, 当一个方法不断地调用它本身时, 必需在某个时刻方法的返回值是确定的.
    即不再调用它本身, 否则这种递归就变成了无穷递归, 类似与死循环.
    因此定义递归方法时有一条最重要的规则: 递归一定要向已知方向递归.

    递归是非常有用的.
    例如希望遍历某个路径下的所有文件.
    但这个路径下文件夹的深度是未知的.
    那么就可以使用递归来实现这个需求.
    系统可以定义一个方法, 该方法接受一个文件路径作为参数.
    该方法可遍历当前路径下的所有文件和文件路径.
    该方法中再次调用该方法本身来处理该路径下的所有文件路径.

    总之, 只要一个方法的方法体实现中再次调用了方法本身, 就是递归方法.
    递归一定要向已知方向递归.

    方法重载

    Java允许同一个类里定义多个同名方法.
    只要形参列表不同就行.
    所以,如果一个类中包含了两个或两个以上的方法的方法名相同,但形参列表不同,则被称为方法重载.

    在 Java程序中确定一个方法需要三个要素:

    • 调用者,也就是方法的所属者. 既可以是类,也可以是对象.
    • 方法名, 方法的标识.
    • 形参列表,当调用方法时,系统将会根据传入的实参列表进行匹配.

    方法重载的要求就是两同一不同: 同一个类中方法名相同,参数列表不同.
    至于方法的其他部分,如方法的返回值类型 / 修饰符等, 与方法的重载没有任何毛关系.

    下面写一个方法重载的示例:

    public class Overload
    {
      //下面定义了两个 test() 方法, 但方法的形参列表不同
      //系统可以区分这两个方法,这被称为方法重载
      public void test()
      {
        System.out.println("无参数方法");
      }
      public void test(String msg)
      {
        System.out.println("重载的 test 方法" + msg);
      }
      public static void main(String[] args)
      {
        Overload ol = new Overload();
        //调用 test() 时没有传入参数,因此系统调用上面没有参数的 test() 方法
        ol.test();
        //调用 test() 时传入了一个字符串参数
        //因此系统调用上面那个待字符串参数的 test() 方法
        ol.test("hello");
      }
    }
    

    你看,系统是不是很聪明.
    根据结果我们可知, 虽然两个 test() 方法的方法名相同, 但因为它们的形参列表不同.
    所以系统可以正常区分出这两个方法.

    展开全文
  • Java对象到底存在堆中还是栈中

    千次阅读 2020-07-30 14:50:02
    创建一个对象的时候,到底是在栈中分配还是在堆中分配需要看2个方面:对象类型和在Java存在的位置 1.如果是基本数据类型,byte、short、int、long、float、double、char,如果是在方法中声明,则存储在栈中,其它...

    创建一个对象的时候,到底是在栈中分配还是在堆中分配需要看2个方面:对象类型和在Java中存在的位置

    • 如果是基本数据类型,byte、short、int、long、float、double、char,如果是在方法中声明,则存储在栈中,其它情况都是在堆中(比方说类的成员变量就在堆中);
    • 除了基本数据类型之外的对象,JVM会在堆中创建对象,对象的引用存于虚拟机栈中的局部变量表中
    • 并不是所有的对象都在堆中存储,可以走栈上分配,在不在栈上分配取决于Hotspot的一个优化技术:“逃逸分析”

    逃逸分析中有方法逃逸和线程逃逸

    • 方法逃逸:
      就是当一个对象在方法中定义后,它可能被外部方法访问到,比如说通过参数传递到其它方法中
    • 线程逃逸:
      就是当一个对象在方法中定义后,它可能赋值给其它线程中访问的变量

    如果不满足逃逸分析就会在栈上分配
    栈上分配的好处就是方法出栈后内存就释放了,不需要通过gc来进行垃圾回收

    需要注意的是:如果执行方法频次比较低的话,是不会触发栈上分配的

    展开全文
  • Java方法存在于哪一区

    千次阅读 2019-10-04 11:52:38
    Java运行时的数据区包括:(其中前两个是线程共享的) ...3.虚拟机栈(VM Stack)描述的是Java方法执行的内存模型:每个方法在执行的同时会创建一个Stack Frame(方法运行时的基础数据结构)用于存...
  • java 方法返回对象的问题

    千次阅读 2016-08-23 17:25:19
    我记得C++里方法返回对象时,会重新构造一个对象,原以为java也是如此,实际测试了下,发现不是,java返回的是对原对象的引用,测试如下: 新建测试类: class Test { String str; public Test() { System.out....
  • 1, Java作为一门面向对象的编程语言,感觉其实也不是100%的面向对象,由于还是保留了一些面向过程的东西,其实也就是一个95%的面向对象,有人说小白编程面向对象,有点东西的是面向百度编程。类与对象到底是个什么...
  • java方法对象方法有什么区别

    千次阅读 2017-10-23 15:35:44
    public class Test { /** * 下面这个方法就是静态方法,应该就是楼主所说的...* 静态方法使所有该类的实例也就是对象共有的方法 */ public static void f1() { System.out.println("我是静态方法~~~~~"); }
  • java对象头信息

    千次阅读 多人点赞 2019-09-02 14:27:14
    做java开发几年了,但一直不知道如下问题: 1. 一个java对象到底占用了多少内存空间,应该如何...3. java对象是在那里设置了指针指向对应的方法区中的元数据的? 4. 在jvm垃圾回收时被标记为可回收但还未执行回...
  • Java基础知识面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 12:11:27
    文章目录Java概述何为编程什么是Javajdk1.5之后的三大版本JVM、JRE和JDK的关系什么是跨平台性?原理是什么Java语言有哪些特点什么是字节码?采用字节码的最大好处是什么什么是Java程序的主类?应用程序和小程序的...
  • Java对象的销毁

    千次阅读 2019-05-10 20:05:32
    对象使用完之后需要对其进行清除。对象的清除是指释放对象占用的内存。在创建对象时,用户必须使用 new 操作符为对象分配内存。不过,在清除对象时,由系统自动进行内存回收,不需要用户额外处理。这也是 Java 语言...
  • java面向对象

    万次阅读 多人点赞 2018-08-21 16:51:59
    包括面向对象概念、类与对象的关系、封装、构造函数、this关键字、static关键字、单例设计模式、继承、多态、内部类、异常、包等java基础知识。 1、面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是...
  • Java中的引用类型的对象存放在哪里

    千次阅读 2020-06-19 12:04:22
    根据上下文来确定。 根据上下文来确定。 根据上下文来确定。 比如 void func() ...对于方法中的局部变量的引用时存放在java运行时数据区的栈中,对于实例变量则是存放在java运行时数据区的堆中。 ...
  • Java 类、对象方法

    万次阅读 2019-10-13 15:57:34
    Java 类、对象方法(一)类的基础知识如何创建对象引用变量和赋值方法 类的基础知识 类是定义对象形式的模板,指定了数据以及操作数据的代码。 Java中使用类的规范来构造对象,而对象是类的实例。 类是逻辑抽象的...
  • Java对象的创建及存储位置

    千次阅读 2019-07-13 22:27:17
    当使用构造器创建对象或静态方法、非常数静态域首次被访问时,Java解释器必须查找类路径,定位.class文件 加载.class文件,创建Class对象,有关静态初始化的所有动作都会执行(静态初始化) 当使用new关键字创建...
  • Java】判断对象中是否存在某属性

    千次阅读 2020-12-31 22:26:46
    判断对象中是否存在某属性
  • Java中调用对象方法的执行过程

    千次阅读 2017-09-25 20:09:08
    编译器查看对象的声明类型和方法名。假设调用x.f(param),且隐式参数x声明为C类的对象。需要注意的是:有可能存在多个名字为f,但是参数类型不一样的方法。例如,可能存在f(int)和f(String)。编译器将会一一列举所有...
  • Java中类,对象方法的内存分配

    千次阅读 2017-06-09 10:22:01
    当我们使用new关键字生成对象时,JVM根据类的代码,去堆内存中开辟一块控件,存放该对象,该对象拥有一些属性,拥有一些方法。但是同一个类的对象对象之间并不是没有联系的,看下面的例子:class Student{ static...
  • 今天记录一下如何使用Java代码判断一个对象是否存在于指定枚举类型中 首先定义了一个枚举类,这个里面是一些系统规定好的数据,不会更改 由于产品是微信小程序,所以有代码总量大小要求,所以全团队以代码简洁为...
  • Class对象是加载的最终产品,类的方法代码,变量名,方法名,访问权限,返回值等等都是在方法区的)才是存在方法区的。 方法区 在一个JVM实例的内部,类型信息被存储在一个称为方法区的内存逻辑区中。类型信息是由...
  • java对象结构

    万次阅读 多人点赞 2017-04-19 22:31:57
    在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。下图是普通对象实例与数组对象实例的数据结构: 对象头 HotSpot虚拟机的...
  • java 校验某对象是否存在某属性

    千次阅读 2019-05-10 15:30:17
    java 校验某对象是否存在某属性 String name="username" if(StrKit.isEmpty(type) ){ JsonResult.error("参数不能为空"); } Class class1=User.class; Field[] fields=class1.getDeclaredFields(); boolean bool=...
  • import java.util.List; public class JudgeList { class Person{ private int id; private int age; private String name; public int getId() { return id; } public void setId(int id) { .
  • java中,对象,还有所有的new出来的对象都是存放在堆里。  方法存放在方法体中。  基本类型的变量的数据和对象的引用存放在栈中  ...
  • Java对象的内存是在哪里分配的?

    千次阅读 2020-09-11 21:12:20
    当我们使用new关键字去实例化一个对象时,对象的内存在哪里分配? 相信很多Java程序员给出的答案都是【堆】,但事实并非绝对如此,JVM为此做了许多优化。 对于绝大多数对象,内存的确是在堆中分配的,但是随着JIT...
  • java对象持久化保存的方法小结

    千次阅读 2017-10-13 17:12:36
    在实际java应用开发的过程中,经常会遇到需要持久保存java对象的情况,比如:用户信息、博客评论内容等等,本文针对java对象的持久化保存方法进行讨论,简述各个方法的优劣,供大家参考。
  • JVM成神之路-Java对象模型

    千次阅读 2018-07-23 15:01:17
    一个Java对象可以分为三部分存储在内存中,分别是:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。 对象头(包含锁状态标志,线程持有的锁等标志) 实例数据 对齐填充 oop-klass model(...
  • 面向对象java语言的核心机制,最重要的内容,java语言的特色】 * 面向过程和面向对象的区别 -- 面向过程:主要关注点是:实现的具体过程,因果关系 -- 面向对象:主要关注对象【独立体】能完成哪些功能。 ...
  • 以上是反射对象中不存在注入的情况下可以使用的方法,一旦涉及到注入的时候,直接使用clazz.newInstance()会导致原本对象中注入的内容变为空值,这里需要通过spring去获取对象bean。 package test ; import ...
  • Java对象创建的流程

    千次阅读 2019-05-07 22:05:39
    文章目录Java对象创建的流程1.Java普通对象的创建1.1new指令1.2分配内存1.3初始化1.4对象的初始设置1.5\方法2.Java对象内存布局2.1对象头2.2实例数据2.3对齐填充 1.Java普通对象的创建 这里讨论的仅仅是普通Java...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,276,355
精华内容 510,542
关键字:

java对象的方法存在哪里

java 订阅