「java程序大对象定位」java问题定位技术

博主:adminadmin 2022-11-22 05:10:08 39

今天给各位分享java程序大对象定位的知识,其中也会对java问题定位技术进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

java运行的五个步骤?

Java程序从源文件创建到程序运行要经过两大步骤:1、源文件由编译器编译成字节码(ByteCode)

2、字节码由java虚拟机解释运行。因为java程序既要编译同时也要经过JVM的解释运行,所以说Java被称为半解释语言( "semi-interpreted" language)。

下面通过以下这个java程序,来说明java程序从编译到最后运行的整个流程。代码如下:

//MainApp.java

public class MainApp {

public static void main(String[] args) {

Animal animal = new Animal("Puppy");

animal.printName();

}

}

//Animal.java

public class Animal {

public String name;

public Animal(String name) {

this.name = name;

}

public void printName() {

System.out.println("Animal ["+name+"]");

}

}

第一步(编译): 创建完源文件之后,程序会先被编译为.class文件。Java编译一个类时,如果这个类所依赖的类还没有被编译,编译器就会先编译这个被依赖的类,然后引用,否则直接引用,这个有点象make。如果java编译器在指定目录下找不到该类所其依赖的类的.class文件或者.java源文件的话,编译器话报“cant find symbol”的错误。

编译后的字节码文件格式主要分为两部分:常量池和方法字节码。常量池记录的是代码出现过的所有token(类名,成员变量名等等)以及符号引用(方法引用,成员变量引用等等);方法字节码放的是类中各个方法的字节码。下面是MainApp.class通过反汇编的结果,我们可以清楚看到.class文件的结构:

第二步(运行):java类运行的过程大概可分为两个过程:1、类的加载 2、类的执行。需要说明的是:JVM主要在程序第一次主动使用类的时候,才会去加载该类。也就是说,JVM并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次。

下面是程序运行的详细步骤:

在编译好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系统就会启动一个jvm进程,jvm进程从classpath路径中找到一个名为AppMain.class的二进制文件,将MainApp的类信息加载到运行时数据区的方法区内,这个过程叫做MainApp类的加载。

然后JVM找到AppMain的主函数入口,开始执行main函数。

main函数的第一条命令是Animal animal = new Animal("Puppy");就是让JVM创建一个Animal对象,但是这时候方法区中没有Animal类的信息,所以JVM马上加载Animal类,把Animal类的类型信息放到方法区中。

加载完Animal类之后,Java虚拟机做的第一件事情就是在堆区中为一个新的Animal实例分配内存, 然后调用构造函数初始化Animal实例,这个Animal实例持有着指向方法区的Animal类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。

当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后根据Animal对象持有的引用定位到方法区中Animal类的类型信息的方法表,获得printName()函数的字节码的地址。

开始运行printName()函数。

特别说明:java类中所有public和protected的实例方法都采用动态绑定机制,所有私有方法、静态方法、构造器及初始化方法都是采用静态绑定机制。而使用动态绑定机制的时候会用到方法表,静态绑定时并不会用到。

「java程序大对象定位」java问题定位技术

JAVA主要面向哪个方面?

1、Java初步

Java是由sun公司开发的一种新型的面向对象的程序设计语言,主要用于web页面的设计。

Java语言的发展颇具传奇性,它与Internet的WWW的迅猛发展是分不开的。由于其发展迅速,有人将它比喻为Internet上的世界语。前面讲到在Internet上Web页面的设计采用的是HTML语言,用户借助于Web浏览器(如Netscape,HotJava,IE等),可以访问到远程web服务器上静态的、具有超链接的Web页面。Java语言的出现改变了 Web页面的这种传统的静态模式,通过在Web页面中附加一些利用Java编写的App1et(称为小程序),可以使Web页面更具多样性和变化性,这样用户就能够访问到动态的,具有交互功能的web页面。

例如一些小的动画,实时更新的图表、声音等。总之,Web页面的设计主要采用HTML语言,利用Java则使其锦上添花。

Sun公司于1995年5月正式推出Java语言,由于其简单、高效、可移植性强等特点,一经推出,很快引起广大用户和众多厂商的普遍关注,特别是Sum公司将其定位于Internet的应用开发上,使得Java得以迅速发展。在同期推出的还有被称为HotJava(热咖啡)的浏览器,Hotjava是一个完全由Java语言编写,基于Java典型应用的web浏览器,并且第一个支持Java Applet。HotJava充分展示了Java在全球Internet网上的强大威力,同时也为Java语言编程提供了一个理想的运行平台。

Java语言推出后,各大软件厂商相继宣布支持Java。首先是Netscape公司在其Web浏览器(Netscape Navigator2.0)中支持Java,不久,sun,SGI和Macromedia三家公司制定了基于Java的开放式多媒体标准。后来许多公司,如IBM,Microsoft,Orac1e等,都宣布支持Java。Netscape公司进一步与sun公司合作,推出了类似于Java的JavaScript语言。目前,Sun公司已成立了专门的Javasoft分部,负责管理Java语言的开发、维护工作(可从Http://站点了解详细信息)。

对于Java这一名字,较为流行的说法是它来源于印度尼西亚的一个重要岛屿,该岛屿的中文译名为"爪哇",岛上盛产咖啡,于是sun公司的开发人员将这种新语言起名为Java,寓意是使其与热气腾腾的咖啡联系起来,将它奉献给全球的计算机用户。

2、Java语言的特点

Java语言的流行除了因为它能够编制嵌入HTML文件中的Applet外,还在于Java语言本身的面向对象、简单、平台无关性、安全性、多线程等特点。Java语言的结构与编写方式,与c++语言类似,因此学习Java语言,不仅要了解Java语言独有的编程特点,同时还要有程序设计基础和面向对象的概念。 BRJava语言的主要特点如下:

(1)简单、高效

Java语言与C++类似,如果用户了解C++和面向对象的概念,就可以很快编写出Java程序;此外,Java又不同于诸如C++语言提供的各种各样的方法,它只提供了基本的方法,这样就减少了编程的复杂性,例如去掉了头文件、指针变量、结构、运算符重载、多重继承等复杂特性。Java语言虽然简单,却很高效,它可以用面向对象的方法来描述用户的每一个动作。

(2)面向对象

Java语言是一种新的面向对象的程序设计语言,它除了几种基本的数据类型外,大都是类似c++中的对象和方法,程序代码大多体现了类机制,以类的形式组织,由类来定义对象的各种行为。Java同样支持类继承,这样也减少了程序设计的复杂性。

(3)平台无关性 Java语言经编译后生成与计算机硬件结构无关的字节代码(Bytecode),这些字节代码被定义为不依赖任何硬件平台和操作系统。当Java程序在运行时,需要由一个解释程序对生成的字节代码解释执行。这体现了Java语言的与平台无关性,使得Java程序可以在任何平台上运行,如MS-DOS,Windows,Unix等,因此具有很强的移植性。

(4)交互式特性

Java是面向对象的网络编程语言,由于它支持TCP/IP协议,使得用户可以通过浏览器访问到Internet上的各种动态对象。并且在网络上用户可以交互式地进行各种动作,而多线程技术的引入使得这种交互式操作更为容易。

(5)多线程机制

Java语言支持多线程机制,多线程机制使得Java程序能够并行处理多项任务。Java程序可以设计成具有多个线程,例如让一个线程负责数据的检索、查寻,另一个线程与用户进行交互,这样,两个线程得以并行执行。多线程机制可以很容易地实现网络上的交互式操作。

(6)动态的内存管理机制

Java语言采用了自动垃圾回收机制进行内存的管理。在C++语言中,程序员在编写程序时要仔细地处理内存的使用,例如当某个内存快使用完毕时,要及时释放,以供其它程序使用,一旦内存管理不当,就有可能造成内存空间浪费或程序运行故障。 BR在Java系统中包括了一个自动垃圾回收程序,它可以自动、安全地回收不再使用的内存块,这样,程序员在编程时就无需担心内存的管理问题,从而使Java程序的编写变得简单,同时也减少了内存管理方面出错的可能性。

(7)安全性

在类似Internet的这种分布式网络环境中,安全性是个不容忽视的问题。Java语言在安全性方面引入了实时内存分配及布局来防止程序员直接修改物理内存布局;通过字节代码验证器对字节代码的检验,以防止网络病毒及其它非法代码侵入。此外,Java语言还采用了许多面向对象的异常处理机制,负责对一些异常事件进行处理,如内存空间不够,程序异常中止等的处理。 BR以上介绍了Java语言的一些主要特点,除此之外它还具有动态性、类库丰富、高性能等特点,这些都使得Java语言在各个方面得以成熟和完善,成为大众日益青睐的程序设计语言之一。

3、Java应用程序和Java AppletJava语言是一种面向对象的程序设计语言,利用Java语言,能够编写出两大类程序:Java应用程序(Application)和Java App1et(小程序):

Java应用程序是由Java语言编写,在经过编译和解释后,可以独立运行在MS-DOS,UNIX等操作平台上。Java应用程序一般以命令行方式运行。

Java Applet是不能独立运行的,App1et必须嵌入到HTML文件中,并且需要启动浏览器才能运行。这样,指定的Applet会自动下载到用户的浏览器中运行,从而产生一些特殊的页面效果,如动画、声音、图表、图像等。通过在Web页面中嵌入Applet,可以使Web页面与用户之间进行动态交互,例如接收用户的输入,然后根据用户的需要产生不同的响应。

就Java Applet而言,它是嵌入到HTML文件中的,HTML文件呈现的内容大多为文字、图片、表格、声音等,但这些内容一般都是静态的、二维的。但当HTML文件中嵌有 Applet后,整个页面会呈现出多样性和变化性,例如交互功能、图表等。在运用方面Java语言和HTML语言的区别在于:

(1)HTML语言属于一种简单的标记语言,一般使用者均可在数小时内学会如何编写HTML文件。而Java语言的结构和编写方法更类似于c++,所以学习Java语言要有程序设计的基础和面向对象的概念。

(2)Web页面的编写主要采用HTML语言,但若想使页面更具变化性、多样性、交互性,可采用Java语言来达到这种效果,起到锦上添花的作用。

4、在HTML文件中嵌入Applet/FONT BRApplet是Java在Internet的WWW中的一个重要应用,同时它的出现也促使了Java成为日益流行的程序设计语言。Applet是指由Java语言编写的,被嵌入到Web页面中,并通过浏览器执行以产生特殊页面效果的小程序。嵌入了Applet的Web页面看上去会更加丰富多彩,具有动态交互能力。

在Internet上拥有大量的App1et,这些Applet大都是由Sun等公司的Java开发人员设计出来的,对于广大用户,特别是初学者,可以将这些App1et下载到自己的计算机中,井通过将这些下载下来的App1et嵌入到自己的Web页面中来,获得自己具有相同效果的Web页面,以丰富自己的页面内容。

如何定位java内存泄露

1、为什么会发生内存泄漏

Java如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题。

编写java程序最为方便的地方就是我们不需要管理内存的分配和释放,一切由jvm来进行处理,当java对象不再被应用时,等到堆内存不够用时,jvm会进行垃圾回收,清除这些对象占用的堆内存空间,如果对象一直被应用,jvm无法对其进行回收,创建新的对象时,无法从Heap中获取足够的内存分配给对象,这时候就会导致内存溢出。而出现内存泄露的地方,一般是不断的往容器中存放对象,而容器没有相应的大小限制或清除机制。容易导致内存溢出。

当服务器应用占用了过多内存的时候,如何快速定位问题呢?现在,Eclipse MAT的出现使这个问题变得非常简单。EclipseMAT是著名的SAP公司贡献的一个工具,可以在Eclipse网站下载到它,完全免费的。

要定位问题,首先你需要获取服务器jvm某刻内存快照。jdk自带的jmap可以获取内存某一时刻的快照,导出为dmp文件后,就可以用Eclipse MAT来分析了,找出是那个对象使用内存过多。

2、内存泄漏的现象:

常常地,程序内存泄漏的最初迹象发生在出错之后,在你的程序中得到一个OutOfMemoryError。这种典型的情况发生在产品环境中,而在那里,你希望内存泄漏尽可能的少,调试的可能性也达到最小。也许你的测试环境和产品的系统环境不尽相同,导致泄露的只会在产品中暴露。这种情况下,你需要一个低负荷的工具来监听和寻找内存泄漏。同时,你还需要把这个工具同你的系统联系起来,而不需要重新启动他或者机械化你的代码。也许更重要的是,当你做分析的时候,你需要能够同工具分离而使得系统不会受到干扰。

一个OutOfMemoryError常常是内存泄漏的一个标志,有可能应用程序的确用了太多的内存;这个时候,你既不能增加JVM的堆的数量,也不能改变你的程序而使得他减少内存使用。但是,在大多数情况下,一个OutOfMemoryError是内存泄漏的标志。一个解决办法就是继续监听GC的活动,看看随时间的流逝,内存使用量是否会增加,如果有,程序中一定存在内存泄漏。

3、发现内存泄漏

1. jstat -gc pid

可以显示gc的信息,查看gc的次数,及时间。

其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。

2.jstat -gccapacity pid

可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,

如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,

PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。

其他的可以根据这个类推,OC是old内纯的占用量。

3.jstat -gcutil pid

统计gc信息统计。

4.jstat -gcnew pid

年轻代对象的信息。

5.jstat -gcnewcapacity pid

年轻代对象的信息及其占用量。

6.jstat -gcold pid

old代对象的信息。

7.stat -gcoldcapacity pid

old代对象的信息及其占用量。

8.jstat -gcpermcapacity pid

perm对象的信息及其占用量。

9.jstat -class pid

显示加载class的数量,及所占空间等信息。

10.jstat -compiler pid

显示VM实时编译的数量等信息。

11.stat -printcompilation pid

当前VM执行的信息。

一些术语的中文解释:

S0C:年轻代中第一个survivor(幸存区)的容量(字节)

S1C:年轻代中第二个survivor(幸存区)的容量(字节)

S0U:年轻代中第一个survivor(幸存区)目前已使用空间(字节)

S1U:年轻代中第二个survivor(幸存区)目前已使用空间(字节)

EC:年轻代中Eden(伊甸园)的容量(字节)

EU:年轻代中Eden(伊甸园)目前已使用空间(字节)

OC:Old代的容量(字节)

OU:Old代目前已使用空间(字节)

PC:Perm(持久代)的容量(字节)

PU:Perm(持久代)目前已使用空间(字节)

YGC:从应用程序启动到采样时年轻代中gc次数

YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)

FGC:从应用程序启动到采样时old代(全gc)gc次数

FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)

GCT:从应用程序启动到采样时gc用的总时间(s)

NGCMN:年轻代(young)中初始化(最小)的大小(字节)

NGCMX:年轻代(young)的最大容量(字节)

NGC:年轻代(young)中当前的容量(字节)

OGCMN:old代中初始化(最小)的大小(字节)

OGCMX:old代的最大容量(字节)

OGC:old代当前新生成的容量(字节)

PGCMN:perm代中初始化(最小)的大小(字节)

PGCMX:perm代的最大容量(字节)

PGC:perm代当前新生成的容量(字节)

S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比

S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比

E:年轻代中Eden(伊甸园)已使用的占当前容量百分比

O:old代已使用的占当前容量百分比

P:perm代已使用的占当前容量百分比

S0CMX:年轻代中第一个survivor(幸存区)的最大容量(字节)

S1CMX:年轻代中第二个survivor(幸存区)的最大容量(字节)

ECMX:年轻代中Eden(伊甸园)的最大容量(字节)

DSS:当前需要survivor(幸存区)的容量(字节)(Eden区已满)

TT:持有次数限制

MTT:最大持有次数限制

如果定位内存泄漏问题我一般使用如下命令:

Jstat -gcutil15469 2500 70

[root@ssss logs]# jstat -gcutil 15469 1000 300

S0 S1 E O P YGC YGCT FGC FGCT GCT

0.00 1.46 26.54 4.61 30.14 35 0.872 0 0.000 0.872

0.00 1.46 46.54 4.61 30.14 35 0.872 0 0.000 0.872

0.00 1.46 47.04 4.61 30.14 35 0.872 0 0.000 0.872

0.00 1.46 65.19 4.61 30.14 35 0.872 0 0.000 0.872

0.00 1.46 67.54 4.61 30.14 35 0.872 0 0.000 0.872

0.00 1.46 87.54 4.61 30.14 35 0.872 0 0.000 0.872

0.00 1.46 88.03 4.61 30.14 35 0.872 0 0.000 0.872

1.48 0.00 5.56 4.62 30.14 36 0.874 0 0.000 0.874

1000 代表多久间隔显示一次,

100 代表显示一次。

S0 — Heap上的 Survivor space 0 区已使用空间的百分比

S1 — Heap上的 Survivor space 1 区已使用空间的百分比

E — Heap上的 Eden space 区已使用空间的百分比

O — Heap上的 Old space 区已使用空间的百分比

P — Perm space 区已使用空间的百分比

YGC — 从应用程序启动到采样时发生 Young GC 的次数

YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)

FGC — 从应用程序启动到采样时发生 Full GC 的次数

FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)

GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)

如果有大量的FGC就要查询是否有内存泄漏的问题了,图中的FGC数量就比较大,并且执行时间较长,这样就会导致系统的响应时间较长,如果对jvm的内存设置较大,那么执行一次FGC的时间可能会更长。

如果为了更好的证明FGC对服务器性能的影响,我们可以使用java visualVM来查看一下:

从上图可以发现执行FGC的情况,下午3:10分之前是没有FGC的,之后出现大量的FGC。

上图是jvm堆内存的使用情况,下午3:10分之前的内存回收还是比较合理,但是之后大量内存无法回收,最后导致内存越来越少,导致大量的full gc。

下面我们在看看大量full GC对服务器性能的影响,下面是我用loadrunner对我们项目进行压力测试相应时间的截图:

从图中可以发现有,在进行full GC后系统的相应时间有了明显的增加,点击率和吞吐量也有了明显的下降。所以java内存泄漏对系统性能的影响是不可忽视的。

3、定位内存泄漏

当然通过上面几种方法我们可以发现java的内存泄漏问题,但是作为一名合格的高级工程师,肯定不甘心就把这样的结论交给开发,当然这也的结论交给开发,开发也很难定位问题,为了更好的提供自己在公司的地位,我们必须给开发工程师提供更深入的测试结论,下面就来认识一下MemoryAnalyzer.exe。java内存泄漏检查工具利器。

首先我们必须对jvm的堆内存进行dump,只有拿到这个文件我们才能分析出jvm堆内存中到底存了些什么内容,到底在做什么?

MemoryAnalyzer的用户我在这里就不一一说明了,我的博客里也有说明,下面就展示我测试的成功图:

其中深蓝色的部分就为内存泄漏的部分,java的堆内存一共只有481.5M而内存泄漏的部分独自占有了336.2M所以本次的内存泄漏很明显,那么我就来看看那个方法导致的内存泄漏:

从上图我们可以发现红线圈着的方法占用了堆内存的67.75%,如果能把这个测试结果交给开发,开发是不是应该很好定位呢。所以作为一名高级测试工程师,我们需要学习的东西太多。

虽然不确定一定是内存泄漏,但是可以准确的告诉开发问题出现的原因,有一定的说服力。

JAVA中的对象究竟是啥啊!

首先有类才有对象,对象就是类的一个实例;一般我们都把类理解为一个模板,对象是由模板产生的真正存在的东西,比如一个Car的类,那么这个类就可以看做生产Car的模板,你用这个类产生的实例就可以看做是一辆车了;在java程序里面,我们不是直接操作对象的,而是拿到对象在内存里面的地址,这个地址就是对象的引用,我们操作这个地址就好像是在操作这个对象一样;这是我自己的理解,希望能帮助到你!

java对象的作用

一切都是对象

“尽管以C++为基础,但Java是一种更纯粹的面向对象程序设计语言”。

无论C++还是Java都属于杂合语言。但在Java中,设计者觉得这种杂合并不象在C++里那么重要。杂合语言允许采用多种编程风格;之所以说C++是一种杂合语言,是因为它支持与C语言的向后兼容能力。由于C++是C的一个超集,所以包含的许多特性都是后者不具备的,这些特性使C++在某些地方显得过于复杂。

Java语言首先便假定了我们只希望进行面向对象的程序设计。也就是说,正式用它设计之前,必须先将自己的思想转入一个面向对象的世界(除非早已习惯了这个世界的思维方式)。只有做好这个准备工作,与其他OOP语言相比,才能体会到Java的易学易用。在本章,我们将探讨Java程序的基本组件,并体会为什么说Java乃至Java程序内的一切都是对象。

2.1 用句柄操纵对象

每种编程语言都有自己的数据处理方式。有些时候,程序员必须时刻留意准备处理的是什么类型。您曾利用一些特殊语法直接操作过对象,或处理过一些间接表示的对象吗(C或C++里的指针)?

所有这些在Java里都得到了简化,任何东西都可看作对象。因此,我们可采用一种统一的语法,任何地方均可照搬不误。但要注意,尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“句柄”(Handle)。在其他Java参考书里,还可看到有的人将其称作一个“引用”,甚至一个“指针”。可将这一情形想象成用遥控板(句柄)操纵电视机(对象)。只要握住这个遥控板,就相当于掌握了与电视机连接的通道。但一旦需要“换频道”或者“关小声音”,我们实际操纵的是遥控板(句柄),再由遥控板自己操纵电视机(对象)。如果要在房间里四处走走,并想保持对电视机的控制,那么手上拿着的是遥控板,而非电视机。

此外,即使没有电视机,遥控板亦可独立存在。也就是说,只是由于拥有一个句柄,并不表示必须有一个对象同它连接。所以如果想容纳一个词或句子,可创建一个String句柄:

String s;

但这里创建的只是句柄,并不是对象。若此时向s发送一条消息,就会获得一个错误(运行期)。这是由于s实际并未与任何东西连接(即“没有电视机”)。因此,一种更安全的做法是:创建一个句柄时,记住无论如何都进行初始化:

String s = "asdf";

然而,这里采用的是一种特殊类型:字串可用加引号的文字初始化。通常,必须为对象使用一种更通用的初始化类型。

2.2 所有对象都必须创建

创建句柄时,我们希望它同一个新对象连接。通常用new关键字达到这一目的。new的意思是:“把我变成这些对象的一种新类型”。所以在上面的例子中,可以说:

String s = new String("asdf");

它不仅指出“将我变成一个新字串”,也通过提供一个初始字串,指出了“如何生成这个新字串”。

当然,字串(String)并非唯一的类型。Java配套提供了数量众多的现成类型。对我们来讲,最重要的就是记住能自行创建类型。事实上,这应是Java程序设计的一项基本操作,是继续本书后余部分学习的基础。

2.2.1 保存到什么地方

程序运行时,我们最好对数据保存到什么地方做到心中有数。特别要注意的是内存的分配。有六个地方都可以保存数据:

(1) 寄存器。这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。

(2) 堆栈。驻留于常规RAM(随机访问存储器)区域,但可通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时,Java编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些Java数据要保存在堆栈里——特别是对象句柄,但Java对象并不放到其中。

(3) 堆。一种常规用途的内存池(也在RAM区域),其中保存了Java对象。和堆栈不同,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!

(4) 静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但Java对象本身永远都不会置入静态存储空间。

(5) 常数存储。常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。

(6) 非RAM存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。Java 1.1提供了对Lightweight persistence的支持。未来的版本甚至可能提供更完整的方案。

2.2.2 特殊情况:主要类型

有一系列类需特别对待;可将它们想象成“基本”、“主要”或者“主”(Primitive)类型,进行程序设计时要频繁用到它们。之所以要特别对待,是由于用new创建对象(特别是小的、简单的变量)并不是非常有效,因为new将对象置于“堆”里。对于这些类型,Java采纳了与C和C++相同的方法。也就是说,不是用new创建变量,而是创建一个并非句柄的“自动”变量。这个变量容纳了具体的值,并置于堆栈中,能够更高效地存取。

Java决定了每种主要类型的大小。就象在大多数语言里那样,这些大小并不随着机器结构的变化而变化。这种大小的不可更改正是Java程序具有很强移植能力的原因之一。

主类型

大小

最小值

最大值

封装器类型

boolean

1-bit

Boolean

char

16-bit

Unicode 0

Unicode 216- 1

Character

byte

8-bit

-128

+127

Byte[11]

short

16-bit

-215

+215 – 1

Short1

int

32-bit

-231

+231 – 1

Integer

long

64-bit

-263

+263 – 1

Long

float

32-bit

IEEE754

IEEE754

Float

double

64-bit

IEEE754

IEEE754

Double

void

Void1

①:到Java 1.1才有,1.0版没有。

数值类型全都是有符号(正负号)的,所以不必费劲寻找没有符号的类型。

主数据类型也拥有自己的“封装器”(wrapper)类。这意味着假如想让堆内一个非主要对象表示那个主类型,就要使用对应的封装器。例如:

char c = 'x';

Character C = new Character('c');

也可以直接使用:

Character C = new Character('x');

这样做的原因将在以后的章节里解释。

1. 高精度数字

Java 1.1增加了两个类,用于进行高精度的计算:BigInteger和BigDecimal。尽管它们大致可以划分为“封装器”类型,但两者都没有对应的“主类型”。

这两个类都有自己特殊的“方法”,对应于我们针对主类型执行的操作。也就是说,能对int或float做的事情,对BigInteger和BigDecimal一样可以做。只是必须使用方法调用,不能使用运算符。此外,由于牵涉更多,所以运算速度会慢一些。我们牺牲了速度,但换来了精度。

BigInteger支持任意精度的整数。也就是说,我们可精确表示任意大小的整数值,同时在运算过程中不会丢失任何信息。

BigDecimal支持任意精度的定点数字。例如,可用它进行精确的币值计算。

至于调用这两个类时可选用的构建器和方法,请自行参考联机帮助文档。

2.2.3 Java的数组

几乎所有程序设计语言都支持数组。在C和C++里使用数组是非常危险的,因为那些数组只是内存块。若程序访问自己内存块以外的数组,或者在初始化之前使用内存(属于常规编程错误),会产生不可预测的后果(注释②)。

②:在C++里,应尽量不要使用数组,换用标准模板库(Standard TemplateLibrary)里更安全的容器。

Java的一项主要设计目标就是安全性。所以在C和C++里困扰程序员的许多问题都未在Java里重复。一个Java可以保证被初始化,而且不可在它的范围之外访问。由于系统自动进行范围检查,所以必然要付出一些代价:针对每个数组,以及在运行期间对索引的校验,都会造成少量的内存开销。但由此换回的是更高的安全性,以及更高的工作效率。为此付出少许代价是值得的。

创建对象数组时,实际创建的是一个句柄数组。而且每个句柄都会自动初始化成一个特殊值,并带有自己的关键字:null(空)。一旦Java看到null,就知道该句柄并未指向一个对象。正式使用前,必须为每个句柄都分配一个对象。若试图使用依然为null的一个句柄,就会在运行期报告问题。因此,典型的数组错误在Java里就得到了避免。

也可以创建主类型数组。同样地,编译器能够担保对它的初始化,因为会将那个数组的内存划分成零。

数组问题将在以后的章节里详细讨论。

2.3 绝对不要清除对象

在大多数程序设计语言中,变量的“存在时间”(Lifetime)一直是程序员需要着重考虑的问题。变量应持续多长的时间?如果想清除它,那么何时进行?在变量存在时间上纠缠不清会造成大量的程序错误。在下面的小节里,将阐示Java如何帮助我们完成所有清除工作,从而极大了简化了这个问题。

2.3.1 作用域

大多数程序设计语言都提供了“作用域”(Scope)的概念。对于在作用域里定义的名字,作用域同时决定了它的“可见性”以及“存在时间”。在C,C++和Java里,作用域是由花括号的位置决定的。参考下面这个例子:

{

int x = 12;

/* only x available */

{

int q = 96;

/* both x q available */

}

/* only x available */

/* q “out of scope” */

}

作为在作用域里定义的一个变量,它只有在那个作用域结束之前才可使用。

在上面的例子中,缩进排版使Java代码更易辨读。由于Java是一种形式自由的语言,所以额外的空格、制表位以及回车都不会对结果程序造成影响。

注意尽管在C和C++里是合法的,但在Java里不能象下面这样书写代码:

{

int x = 12;

{

int x = 96; /* illegal */

}

}

编译器会认为变量x已被定义。所以C和C++能将一个变量“隐藏”在一个更大的作用域里。但这种做法在Java里是不允许的,因为Java的设计者认为这样做使程序产生了混淆。

2.3.2 对象的作用域

Java对象不具备与主类型一样的存在时间。用new关键字创建一个Java对象的时候,它会超出作用域的范围之外。所以假若使用下面这段代码:

{

String s = new String("a string");

} /* 作用域的终点 */

那么句柄s会在作用域的终点处消失。然而,s指向的String对象依然占据着内存空间。在上面这段代码里,我们没有办法访问对象,因为指向它的唯一一个句柄已超出了作用域的边界。在后面的章节里,大家还会继续学习如何在程序运行期间传递和复制对象句柄。

这样造成的结果便是:对于用new创建的对象,只要我们愿意,它们就会一直保留下去。这个编程问题在C和C++里特别突出。看来在C++里遇到的麻烦最大:由于不能从语言获得任何帮助,所以在需要对象的时候,根本无法确定它们是否可用。而且更麻烦的是,在C++里,一旦工作完成,必须保证将对象清除。

这样便带来了一个有趣的问题。假如Java让对象依然故我,怎样才能防止它们大量充斥内存,并最终造成程序的“凝固”呢。在C++里,这个问题最令程序员头痛。但Java以后,情况却发生了改观。Java有一个特别的“垃圾收集器”,它会查找用new创建的所有对象,并辨别其中哪些不再被引用。随后,它会自动释放由那些闲置对象占据的内存,以便能由新对象使用。这意味着我们根本不必操心内存的回收问题。只需简单地创建对象,一旦不再需要它们,它们就会自动离去。这样做可防止在C++里很常见的一个编程问题:由于程序员忘记释放内存造成的“内存溢出”。

2.4 新建数据类型:类

如果说一切东西都是对象,那么用什么决定一个“类”(Class)的外观与行为呢?换句话说,是什么建立起了一个对象的“类型”(Type)呢?大家可能猜想有一个名为“type”的关键字。但从历史看来,大多数面向对象的语言都用关键字“class”表达这样一个意思:“我准备告诉你对象一种新类型的外观”。class关键字太常用了,以至于本书许多地方并没有用粗体字或双引号加以强调。在这个关键字的后面,应该跟随新数据类型的名称。例如:

class ATypeName {/*类主体置于这里}

这样就引入了一种新类型,接下来便可用new创建这种类型的一个新对象:

ATypeName a = new ATypeName();

在ATypeName里,类主体只由一条注释构成(星号和斜杠以及其中的内容,本章后面还会详细讲述),所以并不能对它做太多的事情。事实上,除非为其定义了某些方法,否则根本不能指示它做任何事情。

2.4.1 字段和方法

定义一个类时(我们在Java里的全部工作就是定义类、制作那些类的对象以及将消息发给那些对象),可在自己的类里设置两种类型的元素:数据成员(有时也叫“字段”)以及成员函数(通常叫“方法”)。其中,数据成员是一种对象(通过它的句柄与其通信),可以为任何类型。它也可以是主类型(并不是句柄)之一。如果是指向对象的一个句柄,则必须初始化那个句柄,用一种名为“构建器”(第4章会对此详述)的特殊函数将其与一个实际对象连接起来(就象早先看到的那样,使用new关键字)。但若是一种主类型,则可在类定义位置直接初始化(正如后面会看到的那样,句柄亦可在定义位置初始化)。

每个对象都为自己的数据成员保有存储空间;数据成员不会在对象之间共享。下面是定义了一些数据成员的类示例:

class DataOnly {

int i;

float f;

boolean b;

}

这个类并没有做任何实质性的事情,但我们可创建一个对象:

DataOnly d = new DataOnly();

可将值赋给数据成员,但首先必须知道如何引用一个对象的成员。为达到引用对象成员的目的,首先要写上对象句柄的名字,再跟随一个点号(句点),再跟随对象内部成员的名字。即“对象句柄.成员”。例如:

d.i = 47;

d.f = 1.1f;

d.b = false;

一个对象也可能包含了另一个对象,而另一个对象里则包含了我们想修改的数据。对于这个问题,只需保持“连接句点”即可。例如:

myPlane.leftTank.capacity = 100;

除容纳数据之外,DataOnly类再也不能做更多的事情,因为它没有成员函数(方法)。为正确理解工作原理,首先必须知道“自变量”和“返回值”的概念。我们马上就会详加解释。

1. 主成员的默认值

若某个主数据类型属于一个类成员,那么即使不明确(显式)进行初始化,也可以保证它们获得一个默认值。

主类型 默认值

Boolean false

Char '\u0000'(null)

byte (byte)0

short (short)0

int 0

long 0L

float 0.0f

double 0.0d

一旦将变量作为类成员使用,就要特别注意由Java分配的默认值。这样做可保证主类型的成员变量肯定得到了初始化(C++不具备这一功能),可有效遏止多种相关的编程错误。

然而,这种保证却并不适用于“局部”变量——那些变量并非一个类的字段。所以,假若在一个函数定义中写入下述代码:

int x;

那么x会得到一些随机值(这与C和C++是一样的),不会自动初始化成零。我们责任是在正式使用x前分配一个适当的值。如果忘记,就会得到一条编译期错误,告诉我们变量可能尚未初始化。这种处理正是Java优于C++的表现之一。许多C++编译器会对变量未初始化发出警告,但在Java里却是错误。

2.5 方法、自变量和返回值

迄今为止,我们一直用“函数”(Function)这个词指代一个已命名的子例程。但在Java里,更常用的一个词却是“方法”(Method),代表“完成某事的途径”。尽管它们表达的实际是同一个意思,但从现在开始,本书将一直使用“方法”,而不是“函数”。

Java的“方法”决定了一个对象能够接收的消息。通过本节的学习,大家会知道方法的定义有多么简单!

方法的基本组成部分包括名字、自变量、返回类型以及主体。下面便是它最基本的形式:

返回类型 方法名( /* 自变量列表*/ ) {/* 方法主体 */}

返回类型是指调用方法之后返回的数值类型。显然,方法名的作用是对具体的方法进行标识和引用。自变量列表列出了想传递给方法的信息类型和名称。

Java的方法只能作为类的一部分创建。只能针对某个对象调用一个方法(注释③),而且那个对象必须能够执行那个方法调用。若试图为一个对象调用错误的方法,就会在编译期得到一条出错消息。为一个对象调用方法时,需要先列出对象的名字,在后面跟上一个句点,再跟上方法名以及它的参数列表。亦即“对象名.方法名(自变量1,自变量2,自变量3...)。举个例子来说,假设我们有一个方法名叫f(),它没有自变量,返回的是类型为int的一个值。那么,假设有一个名为a的对象,可为其调用方法f(),则代码如下:

int x = a.f();

返回值的类型必须兼容x的类型。

象这样调用一个方法的行动通常叫作“向对象发送一条消息”。在上面的例子中,消息是f(),而对象是a。面向对象的程序设计通常简单地归纳为“向对象发送消息”。

③:正如马上就要学到的那样,“静态”方法可针对类调用,毋需一个对象。

2.5.1 自变量列表

自变量列表规定了我们传送给方法的是什么信息。正如大家或许已猜到的那样,这些信息——如同Java内其他任何东西——采用的都是对象的形式。因此,我们必须在自变量列表里指定要传递的对象类型,以及每个对象的名字。正如在Java其他地方处理对象时一样,我们实际传递的是“句柄”(注释④)。然而,句柄的类型必须正确。倘若希望自变量是一个“字串”,那么传递的必须是一个字串。

④:对于前面提及的“特殊”数据类型boolean,char,byte,short,int,long,,float以及double来说是一个例外。但在传递对象时,通常都是指传递指向对象的句柄。

下面让我们考虑将一个字串作为自变量使用的方法。下面列出的是定义代码,必须将它置于一个类定义里,否则无法编译:

int storage(String s) {

return s.length() * 2;

}

这个方法告诉我们需要多少字节才能容纳一个特定字串里的信息(字串里的每个字符都是16位,或者说2个字节、长整数,以便提供对Unicode字符的支持)。自变量的类型为String,而且叫作s。一旦将s传递给方法,就可将它当作其他对象一样处理(可向其发送消息)。在这里,我们调用的是length()方法,它是String的方法之一。该方法返回的是一个字串里的字符数。

通过上面的例子,也可以了解return关键字的运用。它主要做两件事情。首先,它意味着“离开方法,我已完工了”。其次,假设方法生成了一个值,则那个值紧接在return语句的后面。在这种情况下,返回值是通过计算表达式“s.length()*2”而产生的。

可按自己的愿望返回任意类型,但倘若不想返回任何东西,就可指示方法返回void(空)。下面列出一些例子。

boolean flag() { return true; }

float naturalLogBase() { return 2.718; }

void nothing() { return; }

void nothing2() {}

若返回类型为void,则return关键字唯一的作用就是退出方法。所以一旦抵达方法末尾,该关键字便不需要了。可在任何地方从一个方法返回。但假设已指定了一种非void的返回类型,那么无论从何地返回,编译器都会确保我们返回的是正确的类型。

到此为止,大家或许已得到了这样的一个印象:一个程序只是一系列对象的集合,它们的方法将其他对象作为自己的自变量使用,而且将消息发给那些对象。这种说法大体正确,但通过以后的学习,大家还会知道如何在一个方法里作出决策,做一些更细致的基层工作。至于这一章,只需理解消息传送就足le

Java程序员怎样理解面向对象分析方法?

前两天刚刚看到一篇文章,还不错,分享一下

“对象(Object)”一词,早在十九世纪就有现象学大师胡塞尔提出并定义。对象是世界中的物体在人脑中的映象,是人的意识之所以为意识的反映,是做为一种概念而存在的先念的东西,它还包括了人的意愿。举例说明吧。当我们认识到一种新的物体,它叫树,于是在我们的意识当中就形成了树的概念。这个概念会一直存在于我们的思维当中,并不会因为这棵树被砍掉而消失。这个概念就是现实世界当中的物体在我们意识当中的映象。我们对它还可以有我们自己的意愿,虽然我们并不需要付诸实现——只要在你的脑中想着把这棵树砍掉做成桌子、凳子等——我们就把它叫做意向。于是,对象就是客观世界中物体在人脑中的映象及人的意向。只要这个对象存在我们的思维意识当中,我们就可以籍此判断同类的东西。譬如,当我们看到另外一棵树是,并不会因为所见的第一棵树不在了失去了供参照的模板而不认识这棵树了。当我们接触某些新事物时,我们的意识就会为这些事物确立一个对象。当然这个过程是怎么形成的,那就不是我们所能讨论的问题了。上面所说的对象研究的是一般意义上的问题,因而它可以外推到一切事物。我们经常所说的“对象”,一班指的是解决信息领域内所遇到问题的方法。特别是应用软件技术来决问题的方法。如我们经常碰到的面向对象的编程(Object-Oriented Programming)、面向对象的分析(Object-Oriented Analysis)、面向对象的设计(Object-Oriented Design)等。应用前面所介绍的关于对象的概念,可以对这些问题做进一步的分析。在面对较复杂的系统,我们可以将它作为一个对象来进行分析。一个系统(解决某个问题的全套解决方案)作为一个对象,可以由多个部分组成。同样,这个对象也可以由多个对象组成。对于同类的事物,可以由一个对象来表示。这样做的益处是显而易见的,它灵活而高效,可以大大减轻设计人员的工作量,简化实际的模型。举一个例子。在关系型数据库的设计当中,我们可以把一个元组当作对象,给它定义一组操作方法。这些方法将适用于所有元组,从而我们不必在更大的范围内去细致的考虑不同的元组(如判断一个元素是否合法):因为它们有一组公共的面向本身的方法,它们“自己”可以“解决”自己的问题。更上一层的对象可以是一个表、视图等。表对象在元组对象的基础上又有它们自己的方法,如增加、删除等。从这个层面上讲,它也只需要做“自己”的事情,因为有元组对象的支持,它无须去考虑像元素是否合法这类的事情。甚至,有时为了满足我们还可以将元素或表群当作时对象并定义它们自己的方法。这样,更能显示面向对象的优势。

上面所讨论的可以说是面向对象的分析方法。在具体的设计过程当中,还应该采用适当的方式。因为面向对象的思想固然很先进,如果做得不好的话,同样不能达到预期的效果。这主要表现在处理对象与对象的关系上没有做好,对象与对象的层次不分明。如上面所举得关系型数据库得例子,如果在元组层面上得对象过多得考虑一个表对象得因素,或一个表层面上对象过多地考虑一个元组对象的因素,甚至去考虑元素层面上的因素,这些都不是好的面向对象的设计方法。这一点,在语言实现方面,Java比C++更有优势,因为它不允许多重继承,从而使对象之间的关系更明确。谁也不会否认C++的功能更强大,但是它也要由次付出巨大代价——当现在代码库发展到一定程度、一定规模时,个对象之间的层次关系将变得异常复杂,给后继使用者得学习、理解带来很大的困难,应用上很难把握。另外,虽然C++具备面向对象的处理能力,但它还是保留了很多面向过程的东西。用C++完全可以不用面向对象的思想来进行程序设计,当然人们不会这样去做——除了那些只是把C++看成是C扩充的初学者,这就为以后的发展埋下了隐患。在者一方面,Java的限制更多一点。就者一点还远远不够。搞开发的是人,开发方法是由人决定的。要应用面向对象的方法开发出优秀的软件,必须要求开发人员具有良好的面向对象的思想。好的工程师可以利用适当的工具开发出优秀的软件——而不在乎他所使用的语言工具——Java、C++、Object Pascal、Ada等。

来源网站:▓ERP总设计师▓

关于java程序大对象定位和java问题定位技术的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

The End

发布于:2022-11-22,除非注明,否则均为首码项目网原创文章,转载请注明出处。