搜档网
当前位置:搜档网 › java类加载机制

java类加载机制

java类加载机制
java类加载机制

Java类加载机制(一)

译:ayi 第一次翻译,翻译得不是很好,请多多指点

我的邮箱:nonopo12345@https://www.sodocs.net/doc/4d5492140.html,

原文:https://www.sodocs.net/doc/4d5492140.html,/pub/a/onjava/2005/01/26/classloading.html

类加载是java特性的一个重量级的组成部分。尽管,java中“advanced topics”的发展,使java的类加载机制的地位有所下降。但每位编程着都应该知道这部分的工作机制,以及怎样去更好与其配合。这可以使我们节省很多时间,二不必要浪费在调试ClassNotFoundException, ClassCastException, 等。

这篇文章将从最基本的开始,比如代码和数据的关系和区别,以及他们怎么样关系起来形成一个实例或者对象。然后将会说到,java中怎样通过类加载器把代码加载到JVM中,以及java中实现的主要的几种类型的类加载器。然后在这篇文章中我们将会了解到java类加载机制的内幕,我们将使用最基本的代码来描述,这些代码执行与类加载器之后,但在加载一个类之前。在接下来的部分将使用一些例子来强调,对于开发者继承和开发自己的类加载器的必要性。接着将告诉你们怎样编写自己的类加载器,以及怎样使用它们去创建一个一般的能加载包括远程客户端辅助代码的类加载器引擎,以及怎样把它在JVM中定义,实例化,然后执行。习惯上,把J2EE-specific components 中说明的作为java类加载的规范,这篇文章正是从这本手册总结来的。

类和数据

一个类代表一段要执行的代码,然而数据则代表与这些代码相关联的某种状态。状态可以改变,代码不能改变。我们把一种特定状态与一个类关联起来时,就得到了这个类的一个实例。所以同一个类的不同实例有不同的状态,但都参照相同的代码。在java中,一个类通常它的代码就包含在一个 .class 文件中,虽然其中也包括异常。然而,在java运行时,每个类都会构造一个超类对象(first-class object),它们其实是https://www.sodocs.net/doc/4d5492140.html,ng.Class的实例。不论何时编译一个java文件,编译器都会在编译后的字节码中嵌入一个public, static, final型的字段class,这个字段表示的就是一个https://www.sodocs.net/doc/4d5492140.html,ng.Class型的实例。因为它是public类型的,我们可以通过标识符来访问它,像这样:

https://www.sodocs.net/doc/4d5492140.html,ng.Class klass = Myclass.class;

只要一个类被加载到JVM,相同的类(强调:相同的类)将不会被重复加载。这将产生一个问题,什么才是相同的类?一个对象有一种特定状态和标识,对象总是与它所属类联系在一起,与这种状况相似,一个被加载到JVM中类也有特定的标识,接下来我们就阐述:

在java中,一个类通过认证的类全名来唯一标识。认证的类全名是由包名和类名两部分组

成。但是在一个类被加载到JVM中则是通过认证的类全名,还有加载这个类的加载器来唯一标识。因此,一个类的类名为C1,包名为Pg,被类加载器类KClassLoader的一个实例k1加载,则C1,也就是C1.class ,的类实例,在JVM中将被解释为(C1,Pg,k1)。这就意味着两个不同的类加载器实例(Cl, Pg, kl1) 和 (Cl, Pg, kl2) ,加载的类在JVM 中将有不同的类实例对象,不是类型可比型(type-compatible)的。在JVM中有多少个类加载器实例呢?下面,我们将讲解这个。

类加载器

在java中,每个类都会被https://www.sodocs.net/doc/4d5492140.html,ng.ClassLoader的一个实例加载。ClassLoader类处于https://www.sodocs.net/doc/4d5492140.html,ng

包下面,开发者可以自由的创建它的子类,添加自己功能的类加载器。

每当敲入java MyMainClass,一个新的JVM开始时,引导类加载器(bootstrap class loader

)首先会把java中的一些关键类,像https://www.sodocs.net/doc/4d5492140.html,ng.Objent,和运行时的代码载入内存。这些运行时类打包在JRE\lib\rt.jar

文件中。因为是一个本地的接口,我们并不能从java文档中得到引导类加载器(bootstrap class loader )信息。也正是这个原因,引导类加载器(bootstrap class loader )的表现也根据JVM的不同而异。

比如,如果我们试图得到一个核心java运行时类的一个类加载器,我们将得到null值,如下:

log(https://www.sodocs.net/doc/4d5492140.html,ng.String.class.getClassLoader());

下面要说到的是java扩展类加载器。在java.ext.dirs 路径下面,我们可以放java扩展类库,这样我们可以获得超出java核心运行时类的特性。扩展类加载器(ExtClassLoader)将会加载java.ext.dirs目录下的所有 .jar 文件。开发者可以为自己的应用增加新的 .jar 文件或者类库,只要他把它们添加到java.ext.dirs目录下面以至于能被扩展类加载器找到。

在Sun的java指南中,文章“理解扩展类加载”(Understanding Extension Class Loading)对以上三个类加载器路径有更详尽的解释,这是其他几个JDK中的类加载器

https://www.sodocs.net/doc/4d5492140.html,.URLClassLoader

java.security.SecureClassLoader

java.rmi.server.RMIClassLoader

sun.applet.AppletClassLoader

https://www.sodocs.net/doc/4d5492140.html,ng.Thread,包含了public ClassLoader getContextClassLoader()方法,这一方法返回针对一具体线程的上下文环境类加载器。上下文加载器是线程创建着提供的,用以来加载线程运行时需要的类和资源。如果没有设定,默认的是父线程的上下文类加载器。最原始的上下文类加载器由加载application应用程序的类加载器建立。

类加载器怎样工作

所有的类加载器,除了引导类加载器,都一个父类加载器。而且,它们都是https://www.sodocs.net/doc/4d5492140.html,ng.ClassLoader类型的。上面两句话是不同的,而且对与开发者开发的任何一个类加载器的正常工作来说都非常重要。在这里,最重要的是怎样正确的设置父类加载器。类加载器的父类加载器实例会负责加载此类加载器类。(记住:一个类加载器本身也是一个类。)

在一个类加载器外部请求一个类时,使用loadClass() 方法。这个方法的具体工作,我们可以从源代码来看:

protected synchronized Class loadClass

(String name, boolean resolve)

throws ClassNotFoundException{

// First check if the class is already loaded

Class c = findLoadedClass(name);

if (c == null) {

try {

if (parent != null) {

c = parent.loadClass(name, false);

} else {

c = findBootstrapClass0(name);

}

} catch (ClassNotFoundException e) {

// If still not found, then invoke

// findClass to find the class.

c = findClass(name);

}

}

if (resolve) {

resolveClass(c);

}

return c;

}

设置父类加载器,我们有两种方法,在ClassLoader的构造方法中。

public class MyClassLoader extends ClassLoader{

public MyClassLoader(){

super(MyClassLoader.class.getClassLoader());

}

}

or

public class MyClassLoader extends ClassLoader{

public MyClassLoader(){

super(getClass().getClassLoader());

}

}

第一种方法更常被使用因为在构造方法中使用getClass() 方法是不提倡的,因为对象初始化仅在构造方法结束后才会完成。因此,如果正确设置了类加载器的父类加载器,不论什么时候从类加载器实例请求一个类时,如果此类加载器不能加载此类,则首先会交给它的父类加载器处理。如果此父类加载器也不能加载那个类,则又会交给上一层的父类加载器,依此类推。但是如果findBootstrapClass0()方法也未能加载那个类时,就会唤醒findClass()去处理。findClass()的默认的实现是抛出ClassNotFoundException 异常。所以开发者需要继承https://www.sodocs.net/doc/4d5492140.html,ng.ClassLoader来实现用户自编写的类加载器。findClass()默认的实现如下:

protected Class findClass(String name)

throws ClassNotFoundException {

throw new ClassNotFoundException(name);

}

深入findClass()方法,类加载器需要从其它资源获取字节码。这些资源可以是文件系统、网络URL、数据库、其它的可以把字节码转换为流的应用程序,或者能够产生与java特性

相适应的字节码的类似资源。你可以使用BCEL (Byte Code Engineering Library),它能非常方便的根据运行时的迹象创建类。BCEL已经成功的应用在了一些地方,如编译器、优化程序、模糊程序(obsfuscators)、代码生成器、分析工具。只要这个这些字节码被重新获取,findClass()方法将会调用defineClass()方法,而且这时运行时(runtime )环境是非常特殊的对于具体哪一个类加载器实例去调用defineClass()这个方法。所有,两个类加载器实例从两个相同的、或者不同的资源加载字节码时,这些加载的类都是不同的。

在java语言详解(Java language specification)中,给出了在java执行引擎(Java Execution Engine)中加载、链接、初始化的类和接口等过程的详细解释。

图1 展示了一个带有main方法的应用程序类MyMainClass。正如前面所说的,MyMainClass.class 将被AppClassLoader加载,MyMainClass 创建两个加载器类实例,CustomClassLoader1和 CustomClassLoader2,他们都能从某些资源(比如说:网络)中加载第四个类Target 的字节码。这就意味着Target 这个类的定义超出了应用程序的class path 或者扩展class path 范围。在这种情况下,如果MyMainClass 让客户加载器实例去加载Target 类。Target 将同时被CustomClassLoader1和CustomClassLoader2加载和定义。在java中这样就会有严重的问题。如果在Target 中包含一段静态(static)的初始化代码,如果我们要求这段代码执行且仅贝被执行一次,在我们目前的情况下,这段代码将被执行两次。在两个CustomClassLoader中,都执行了一次。如果Target 被两个CustomClassLoader同时初始化,他将会有两个实例target1 和target2 ,正如下面图1所示的,target1 和target2 是不可比的。也就是说,在java中不能执行这段代码:

Target target3 = (Target) target2;

上面的代码将会抛出ClassCastException异常。这是因为JVM把他们看做两个独立的不同的类类型,因为它们被不同类型的ClassLoader 实例加载。如果MyMainClass 使用的不是不同类型的ClassLoader(这里:CustomClassLoader1 和CustomClassLoader2)实例,使用同一类型的ClassLoader 的两个实例来加载,情况也将是一样的。这将在后面通过代码来证实。

图1 在同一个JVM中多个ClassLoader加载同一个类Target

关于类加载、定义、链接的过程更加详细的解释在Andreas Schaefer的文章Inside Class Loaders中。

为什么我们要使用我们自己的类加载器?

开发者编写自己的类加载器的一个理由是控制JVM的行为。在java中一个类被区分是通过包名加类名。因为这些类实现了java.io.Serializable接口,serialVersionUID 将在类的版本化中担当一个重要角色。这种流唯一标志是一个由类名、接口名、方法以及字段生成的一个64位的哈希码。除了这些,没有其它的直接的结构来版本化一个类。单从技术上来说,如果上面所说的匹配的话,这些类就是相同的版本。

想一下这种情况,当我们想要去开发一个一般性的执行引擎,它能够执行任何实现了某个特定接口的任务。当这些任务被提交到引擎,引擎首先需要去加载这些任务的代码。假如很多

不同的客户提交不同的任务(就是:不同的代码)给我们的引擎,碰巧,这些任务都有相同的类名和包名。问题产生了,引擎将要为不同的客户调用上下文加载不同的客户版本,使客户能得到它们想要的正确结果吗?这将在下面,通过加载相同的客户代码来证实。在samepath 和 differentversions这两个目录中,均包括独立的代码来证实这个原理。

图2 展示了示例在samepath, differentversions, 和 differentversionspush 这三个子目录中。

图2 示例的目录结构

在samepath目录,我们有均version.Version 类分别在它的子目录v1和v2下面。这两个类均有相同的类名和包名。唯一的不同是:

public void fx(){

log("this = " + this + "; Version.fx(1).");

}

在v1中,我们有Version.fx(1)在log 语句中;而在v2中,是Version.fx(2)。在相同的路径下,这两个不同版本的类仅有这点区别。现在执行Test 如下:

set CLASSPATH=.;%CURRENT_ROOT%\v1;%CURRENT_ROOT%\v2

%JAVA_HOME%\bin\java Test

在图3中,我们将看到控制台的输出,Version.fx(1) 被加载了,因为ClassLoader在classpath中首先找到了v1中的类。

图3 在版本1的test类的路径放在前面

我们稍微改变一下classpath中个路径的先后顺序,再次运行一次:

set CLASSPATH=.;%CURRENT_ROOT%\v2;%CURRENT_ROOT%\v1

%JAVA_HOME%\bin\java Test

控制台的输出改变了,如图4。现在,Version.fx(2) 被执行了,因为class loader 先找到了这个版本的类。

图4 在版本2的test类的路径放在前面

从上面的可以看出,class loader 会加载在classpath中先找到的类。如果我们不这样,从v1、v2中删除version.Version ,而把它们打包在一个myextension.jar 的.jar文件中,并把它们放到java.ext.dirs目录下面,重新测试,我们将看到,它将不再被AppClassLoader加载,而是被扩展类加载器加载。如图5所示:

图5 AppClassLoader 和 ExtClassLoader 类加载器

进一步来研究这个例子,目录differentversions 下面有一个RMI 执行引擎。客户能够提交任何实现了common.TaskIntf 接口的任务给这个引擎。两个子目录client1 和client2 均包含了稍有区别的这个类client.TaskImpl。它们的区别如下代码所示:

static{

log("client.TaskImpl.class.getClassLoader

(v1) : " + TaskImpl.class.getClassLoader());

}

public void execute(){

log("this = " + this + "; execute(1)");

}

在client1的log语句中执行的是getClassLoader(v1) 是 execute(1),在client2的log语句中执行的是getClassLoader(v2) 是 execute(2)。而且,在启动RMI服务器引擎的脚本中,我们不妨把client2路径放在前面:

CLASSPATH=%CURRENT_ROOT%\common;%CURRENT_ROOT%\server;

%CURRENT_ROOT%\client2;%CURRENT_ROOT%\client1

%JAVA_HOME%\bin\java server.Server

在图6、7、8的屏幕截屏中展示了这时的情况。这时,在两个客户虚拟机中,单独的client.TaskImpl类,被加载、初始化,并传递到了服务器端虚拟机的执行引擎中。从服务器端的控制台可以看出,client.TaskImpl仅被加载了一次。这个单独“版本”的代码在服务器端

被用来产生许多client.TaskImpl 实例,来执行任务。

图6 服务器端控制台输出

图6展示了服务器端引擎控制台的输出,它加载、执行两个各自的客户请求(如图7、8所示)。要指出的是,在这里代码仅被执行了一次(很明显,从静态初始化语句块中的log语句来看),但是这个方法被执行了两次,每个客户的一次调用。

图7 客户1的控制台

在图7中,服务器端提供的类TaskImpl 包括语句client.TaskImpl.class.getClassLoader(v1) 被加载入了客户虚拟机。在图8中,客户虚拟机加载了不同的,包含client.TaskImpl.class.getClassLoader(v2)的,TaskImpl 类代码。

图8 客户2的控制台

这里,各自的client.TaskImpl 类被加载、初始化,并且交给服务器端虚拟机来执行。再次看一下图6所展示的服务器端的控制台,显示了client.TaskImpl 加载且仅被加载了一次。这个单独的代码将被用来在服务器端生成client.TaskImpl 实例。Client1将不高兴了,因为它的语句client.TaskImpl(v1)并没有被执行,而是其它的代码在执行当它在客户端调用时。我们怎样处理这种情况呢?答案是实现客户的类加载器。

客户类加载器

较好的控制类加载的解决办法是实现自己的客户类加载器。任何客户类加载器都必须直接或间接继承https://www.sodocs.net/doc/4d5492140.html,ng.ClassLoader。而且在构造函数中,我们也必须把父类设置为类加载器。然后,我们要重写findClass()方法。在differentversionspush目录中包括了一个名为FileSystemClassLoader的类加载器。目录结果如图9所示:

图9 客户类加载器关系

下面是在common.FileSystemClassLoader实现的主要方法:public byte[] findClassBytes(String className){

try{

String pathName = currentRoot +

File.separatorChar + className.

replace('.', File.separatorChar) + ".class";

FileInputStream inFile = new

FileInputStream(pathName);

byte[] classBytes = new

byte[inFile.available()];

inFile.read(classBytes);

return classBytes;

}

catch (java.io.IOException ioEx){

return null;

}

}

public Class findClass(String name)throws

ClassNotFoundException{

byte[] classBytes = findClassBytes(name); if (classBytes==null){

throw new ClassNotFoundException();

}

else{

return defineClass(name, classBytes, 0, classBytes.length);

}

}

public Class findClass(String name, byte[]

classBytes)throws ClassNotFoundException{

if (classBytes==null){

throw new ClassNotFoundException(

"(classBytes==null)");

}

else{

return defineClass(name, classBytes,

0, classBytes.length);

}

}

public void execute(String codeName,

byte[] code){

Class klass = null;

try{

klass = findClass(codeName, code);

TaskIntf task = (TaskIntf)

klass.newInstance();

task.execute();

}

catch(Exception exception){

exception.printStackTrace();

}

}

这个类被客户用来转换client.TaskImpl(v1)为byte[]。这个byte[]将被传递给服务器端执行引擎。在服务器端,相同的类将被用来从字节数组逆转定义这个类。客户端代码如下:

public class Client{

public static void main (String[] args){

try{

byte[] code = getClassDefinition

("client.TaskImpl");

serverIntf.execute("client.TaskImpl",

code);

}

catch(RemoteException remoteException){

remoteException.printStackTrace();

}

}

private static byte[] getClassDefinition

(String codeName){

String userDir = System.getProperties().

getProperty("BytePath");

FileSystemClassLoader fscl1 = null;

try{

fscl1 = new FileSystemClassLoader

(userDir);

}

catch(FileNotFoundException

fileNotFoundException){

fileNotFoundException.printStackTrace();

}

return fscl1.findClassBytes(codeName);

}

}

从服务器端来看,从客户端接受的代码将交给客户类加载器。客户类加载器首先从接收到的字节数组中逆定义类,实例化,然后执行。这里值得指出的是,对于每个客户请求,都将使用各自的FileSystemClassLoader实例来加载提供的client.TaskImpl。而且,client.TaskImpl类并不在服务器端的classpath范围内。这就意味着,当我们在FileSystemClassLoader中调用findClass() 时,findClass()会从内部调用defineClass(), client.TaskImpl将被各自的类加载器实例加载。当有一个新的FileSystemClassLoader 实例来加载时,照样从逆定义类开始重新做一遍。所以,对每个客户调用,类client.TaskImpl 都被重新定义了,使我们能够在同一个虚拟机中执行“不同版本”的client.TaskImpl 代码。

public void execute(String codeName, byte[] code)throws RemoteException{

FileSystemClassLoader fileSystemClassLoader = null;

try{

fileSystemClassLoader = new FileSystemClassLoader();

fileSystemClassLoader.execute(codeName, code);

}

catch(Exception exception){

throw new RemoteException(exception.getMessage());

}

}

目录differentversionspush下的examples。服务器和客户端控制台输出,如图10 、11、12中所示:

图10 服务器端的Custom class loader 的执行结果

图10 展示了客户类加载器的虚拟机控制台。我们可以看到client.TaskImpl 被执行了不只一次。事实上,对每个客户执行上下文环境,这个类被新加载和初始化一次。

图11 客户类加载器引擎Client 1

在图11中,包含了语句client.TaskImpl.class.getClassLoader(v1) 的TaskImpl类的这些代码在客户端被加载,然后被放到服务器端执行。图12,包含了语句client.TaskImpl.class.getClassLoader(v2) 的TaskImpl的不同类在客户端2的加载情况,并在服务器端执行。

图12 客户类加载引擎,client2

这例子向我们展示了,当在同一个JVM中有“不同版本”的代码时,我们怎样使用各自的类加载器实例来实现一一对应的边对边执行。

类加载器在J2EE中

在j2ee中,类加载器倾向于在不同的时间段移除和重新加载类。这种情况在某些实现中存在,在某些中没有。web服务器可能会移除以前的一个已经加载的servlet实例,可能因为是管理员明确地要这么做,也可能是这个servlet已经空闲的很长一段时间。当第一次请求一个jsp(假设这个jsp还没有被初次编译),JSP引擎将转译这个jsp为一个页面实现类,切实一个标准的servlet类。只要这个页面实现类servlet一创建,它将被JSP引擎编译为一个class文件、备用。每当客户请求这个jsp时,编译器将首先会检查这个jsp是否被修改了。如果是的话,JSP引擎将把它重新转译,以确保给客户端的响应是最新的jsp页面实现所生成的。以.ear, .war, .rar形式的企业应用程序部署单元,也需要在需要的时候或配置策略改变时,被加载或重新加载。对所有的情况来说,都有可能被加载、移除以及重新加载,除非我们已经控制了应用程序服务器的类加载策略。这通过扩展类加载器可以做到,因为它能执行在它范围内的代码。

Brett Peterson已经在发表在 https://www.sodocs.net/doc/4d5492140.html,的"Understanding J2EE Application Server Class Loading Architectures"上给出了J2EE application server 的类加载模式的解释。

总结

这篇文章讲述了怎样把类加载到JVM并唯一标识,和具有相同类名和包名时的一些限制。

因为没有直接的类版本结构,如果我们想按我们自己得到想法来加载类时,我们不得不使用客户类加载器来扩展实现。许多J2EE应用程序服务器有“热部署”的能力,能够使我们以一个新的类定义来重新加载应用程序,而不需要关闭服务器。这些应用程序服务器利用了客户类加载器。尽管我们不使用应用程序服务器,我们可以创建和使用客户类加载器来更好的控制java应用程序的类加载。 Ted Neward的书Server-Based Java Programming

非常好的描述了java类加载的详细细节,并告诉了我们一些在 J2EE APIs中没有提到的东西以及怎样更好的去使用它们。

参考

Sample code for this article

JDK 1.5 API Docs

The Java language specification

"Understanding Extension Class Loading " in the Java tutorial "Inside Class Loaders" from ONJava

"Inside Class Loaders: Debugging" from ONJava

"What version is your Java code?" from JavaWorld

"Understanding J2EE Application Server Class Loading Architectures"

from TheServerSide

Byte Code Engineering Library

Server-Based Java Programming by Ted Neward

作者:Binildas Christudas

北大青鸟推荐:Java精选笔试题(含答案解析)

北大青鸟推荐:Java精选笔试题(含答案解析)如果你是计算机专业出生,但是还没有找到工作的话,你就得补补技术了,一些关于面试、笔试的题要多刷一刷。有可能你知道答案,但是由于语言组织能力有所欠缺,所以面试官的印象不是很好,下面分享一些Java精选的鄙视题,希望对面试这者有帮助。 1,volatile关键字是否能保证线程安全?() 答案:否 volatile关键字用在多线程同步中,可保证读取的可见性,JVM只是保证从主内存加载到线程工作内存的值是最新的读取值,而非cache中。但多个线程对volatile的写操作,无法保证线程安全。 假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值,在线程1对count进行修改之后,会write到主内存中,主内存中的count变量就会变为6;线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6;导致两个线程及时volatile关键字修改之后,还是会存在并发的情况。 2,下面哪个流类属于面向字符的输入流( ) A、BufferedWriter B、FileInputStream C、ObjectInputStream D、InputStreamReader 答案:D Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式。

面向字节的操作为以8位为单位对二进制的数据进行操作,对数据不进行转换,这些类都是InputStream和OutputStream的子类。 面向字符的操作为以字符为单位对数据进行操作,在读的时候将二进制数据转为字符,在写的时候将字符转为二进制数据,这些类都是Reader和Writer的子类。 3,Java能不能不通过构造函数创建对象() A、能 B、不能 答案:A Java创建对象的几种方式: (1) 用new语句创建对象,这是最常见的创建对象的方法。 (2) 运用反射手段,调用https://www.sodocs.net/doc/4d5492140.html,ng.Class或者https://www.sodocs.net/doc/4d5492140.html,ng.reflect.Constructor类的newInstance()实例方法。 (3) 调用对象的clone()方法。 (4) 运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法。 (1)和(2)都会明确的显式的调用构造函数;(3)是在内存上对已有对象的影印,所以不会调用构造函数;(4)是从文件中还原类的对象,也不会调用构造函数。 4,下列哪个叙述是正确的() A.子类继承父类的构造方法。 B.abstract类的子类必须是非abstract类。 C.子类继承的方法只能操作子类继承和隐藏的成员变量。 D.子类重写或新增的方法也能直接操作被子类隐藏的成员变量。 答案:C 子类是不继承父类的构造方法的,而是必须调用其父类的构造方法。

Java API 试题

永隆 JAVA笔试题 一、选择题 1、关于Java 类的加载过程,下面哪些描述是正确的() A、在 Java 中,有四种类型的类加载器:BootStrapClassLoader、ExtClassLoader、AppClassLoader 以及用户自定义的ClassLoader。//Extension ClassLoader, System ClassLoader+用户自定义的classloader B、使用 new 关键字创建类实例时,其实就显示地包含了类的加载过程 C、在 Java 中,类的实例化流程分为两个部分:类的加载和类的实例化。类的加载又分为显式加载和隐式加载。 D、Class.forName 来加载类时,是通过 ExtClassLoader进行加载的。 //system classLoader 加载 2、关于HashMap的实现机制,下面哪些描述是正确的() A、HashMap中key-value 当成一个整体进行处理,系统总是根据数组的坐标来获得key-value 的存储位置。//没有存储顺序,无下标之说! B、HashMap基于哈希表的 Map 接口的实现,允许使用 null 值和 null 键。 C、如果HashMap中,如果Key的hash相同的话,HashMap将会出错。//会替换相应的value D、HashMap每次容量的扩增都是以2的倍数来增加。//大约获得2倍的桶数! 3、下面的代码执行输出正确的是() 1. public class test( 2. public int aMethod()[ 3. static int i=0; 4. i++; 5. return I; 6. ) 7. public static void main (String args[]){ 8. test test = new test(); 9. test.aMethod(); 10.int j = test.aMethod(); 11.System.out.printIn(j); 12.] 13.} A. 编译错误 B. 编译成功,打印出是“0” C. 编译成功,打印出是“1” D. 编译成功,打印出是“2” A 4、如何获取下面表单 select

类的加载

类从加载到虚拟机到卸载,它的整个生命周期包括:加载(Loading),验证(Validation),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using)和卸载(Unloading)。其中,验证、准备和解析部分被称为连接(Linking)。 加载: 在加载阶段,虚拟机主要完成三件事: 1.通过一个类的全限定名来获取定义此类的二进制字节流。 2.将这个字节流所代表的静态存储结构转化为方法区域的运行时数据结构。 3.在Java堆中生成一个代表这个类的https://www.sodocs.net/doc/4d5492140.html,ng.Class对象,作为方法区域数据的访问入口。 验证: 验证阶段作用是保证Class文件的字节流包含的信息符合JVM规范,不会给JVM造成危害。如果验证失败,就会抛出一个https://www.sodocs.net/doc/4d5492140.html,ng.VerifyError异常或其子类异常。验证过程分为四个阶段: 1.文件格式验证:验证字节流文件是否符合Class文件格式的规范,并且能被当前虚拟机正确的处理。 2.元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范。 3.字节码验证:主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。

4.符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。 准备: 准备阶段为变量分配内存并设置类变量的初始化。在这个阶段分配的仅为类的变量(static修饰的变量),而不包括类的实例变量。对已非final的变量,JVM会将其设置成“零值”,而不是其赋值语句的值: pirvate static int size = 12; 那么在这个阶段,size的值为0,而不是12。final修饰的类变量将会赋值成真实的值。 解析: 解析过程是将常量池内的符号引用替换成直接引用。主要包括四种类型引用的解析。类或接口的解析、字段解析、方法解析、接口方法解析。 初始化: 在准备阶段,类变量已经经过一次初始化了,在这个阶段,则是根据程序员通过程序制定的计划去初始化类的变量和其他资源。这些资源有static{}块,构造函数,父类的初始化等。 至于使用和卸载阶段阶段,这里不再过多说明,使用过程就是根据程序定义的行为执行,卸载由GC完成。

【IT专家】来自加载类的Java Call类函数

本文由我司收集整编,推荐下载,如有疑问,请与我司联系 来自加载类的Java Call类函数 2011/02/25 1277 How would I, if possible, call a function in my Main class in the class that I load? (I know that is impossible to understand, so I’ll explain) ?如果可能的话,我如何在我加载的类中调用Main类中的函数? (我知道这是不可能理解的,因此我会解释) I.E: public class SomeClass public SomeClass //load a class here public void FuncToCall() {}//In a separate file, dynamically loaded.public class LoadedClass public LoadedClass //Call a function in the class that loads this SomeClass.FuncToCall(); So I would end up having 2 files: SomeClass.class and LoadedClass.class. I guess my main question is, how do I reference SomeClass in LoadedClass? ?因此我最终会得到2个文件:SomeClass.class和LoadedClass.class。我想我的主要问题是,如何在LoadedClass中引用SomeClass? **EDIT: So maybe I should better explain the use case. A java program dynamically loads the “SomeClass” script from a folder. The script then downloads a .jar file from the internet and opens and runs the “LoadedClass” script within that. How do I use functions in SomeClass in LoadedClass if SomeClass isn’t in the same .jar or in a .jar at all? ?因此也许我应该更好地解释用例。java程序从文件夹动态加载“SomeClass”脚本。然后,该脚本从Internet下载.jar文件,并在其中打开并运行“LoadedClass”脚本。如果SomeClass不在同一个.jar或.jar中,我如何在LoadedClass中的SomeClass中使用函数? ?你这样做.. ?If they are in different packages you have to either import the class or use the fully qualified name. ?如果它们位于不同的包中,则必须导入该类或使用完全限定的名称。

java如何写存储过程

Java中对存储过程的调用 一:Java如何实现对存储过程的调用: A:不带输出参数的 ---------------不带输出参数的---------------------------------- create procedure getsum @n int =0<--此处为参数--> as declare @sum int<--定义变量--> declare @i int set @sum=0 set @i=0 while @i<=@n begin set @sum=@sum+@i set @i=@i+1 end print 'the sum is '+ltrim(rtrim(str(@sum))) --------------在SQL中执行:-------------------- exec getsum 100 ------------在JAVA中调用:--------------------- JAVA可以调用但是在JAVA程序却不能去显示该存储过程的结果因为上面的存储 过程的参数类型int 传递方式是in(按值)方式 import java.sql.*; public class ProcedureTest { public static void main(String args[]) throws Exception { //加载驱动 DriverManager.registerDriver(new sun.jdbc.odbc.JdbcOdbcDriver()); //获得连接 Connection conn=DriverManager.getConnection("jdbc:odbc:mydata","sa",""); //创建存储过程的对象 CallableStatement c=conn.prepareCall("{call getsum(?)}"); //给存储过程的参数设置值 c.setInt(1,100); //将第一个参数的值设置成100 //执行存储过程 c.execute(); conn.close();

Java程序是如何运行的

Java程序是如何运行的 JVM是Java的运行时虚拟机,所有的Java程序都是在JVM沙箱中运行,每个Java程序就是一个独立的JVM进程。 谈到Java程序是如何运行的,首先需要理解的肯定是JVM是如何运行的,什么是JVM;要理解我们编写的Java程序,运行起来以后到底是什么样子,本质上就是弄清楚JVM是什么样子。 Java程序的代码是什么样的 Java诞生之初最大的卖点就是编写的代码跨平台可移植性,实现这种可移植性,是因为Java通过平台特定的虚拟机,运行中间的字节码,而不是直接编译成本地二进制代码实现,中间字节码也就是java文件编译后生成的.class文件,Jar包的话,实际上只是一系列.class文件的集合。 编写Java程序,首先需要一个入口点,在运行的时候通过指定MainClass来指定入口点,代码层面主类必须实现一个静态的main函数,运行时虚拟机会从MainClass.main开始执行指令,其他的逻辑只是import和函数调用了。 SDK自带的javac命令,负责将我们编程的Java代码,也就是.java文件,编译成平台无关的字节码;字节码可以在任何操作系统平台上,通过平台对应的JVM执行;JVM执行的时候,运行字节码,根据自己的平台特性,将字节码转换成平台相关的二进制码运行。 javac编译器运行的过程大致分为:词法分析(Token流)、语法分析(语法树)、语义分析(注解语法树),还有代码生成器,根据注解语法树,生成字节码, 语义分析阶段,编译器会做一些操作,将人类友好的代码,做一些处理,转换成更符合机器执行机制的代码,例如全局变量,魔法变量,依赖注入,注解这些魔法机制。大致分为以下步骤: 1. 给类添加默认构造函数 2. 处理注解 3. 检查语义的合法性并进行逻辑判断 4. 数据流分析 5. 对语法树进行语义分析(变量自动转换并去掉语法糖) JVM是什么 JVM = 类加载器 classloader + 执行引擎 execution engine + 运行时数据区域 runtime data area JVM就是运行编译好字节码的虚拟机,不同的操作系统和平台上,虚拟机将平台无关的字节码,编译成特定平台的指令去执行。我觉得,JVM首先是一个独立运行在操作系统上的进程。执行java命

Java的数据结构相关的类实现原理

Java的数据结构相关的类实现原理 List接口 List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。 和下面要提到的Set不同,List允许有相同的元素。 除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。 实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。 LinkedList List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。 此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。 所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。 注意,此实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使 用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示: List list = Collections.synchronizedList(new LinkedList(...)); 此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛 出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。 注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。 ArrayList

通过oracle,调用java类,并加载jar包到oracle中以支持java类

根据鬼子要求,最初的单纯使用oracle发送和接收mail被抛弃(上一篇文章描述了具体实现),转而要求使用oracle调用java,并通过javamail来实现mail的相关处里。这里问题就出现了,我编写过java,我编写过pl/sql,但是,从来没听说过使用oracle来调用java。同事没有一个作过的。不过,后来察看了相关资料,才知道,这个技术还确实有。于是做如下的相关记录。 我要做的第一个是把我之前编好的一个压缩功能java类和其需要的jar包文件加载到oracle中,并使其能够被成功调用。如何压缩文件,稍后处理。我们先说如何加载java类和jar包到oracle。 首先,压缩功能需要的环境配置: 1、操作系统需要拥有支持loadjava命令的jdk。 2、加载jlha.jar包,到oracle数据库中。 操作过程:在dos环境下,输入命令: loadjava -r -f -o -user usscares/usscares@usscares jlha.jar 这个命令就是oracle加载jlha.jar包的命令。 编写好需要的,负责压缩的类:Directoryzip 在其源文件头插入一行:create or replace and compile java source named directoryzip as 并执行在数据库commandwindow中,则导入数据库。 既然已经成功导入类到oracle中,那么接下来就是编写函数,使得oracle能够调用此类中的代码: [SQL]view plaincopyprint? 1.create or replace function zipblob (returnBLob BLOB,inBlob BLOB,filename VA RCHAR2) return BLOB

java实验指导书

实验一java开发环境及语言基础 实验目的 (1)确保正确配置java开发环境。 (2)了解javac和java命令的使用。 (3)熟悉java中的运算符。 (4)掌握条件语句和循环语句的使用。 (5)掌握通过命令行参数接受数据。 (6)掌握用Scanner类接受数据。 实验内容 (1)在控制台中输入java命令,查看输入结果。 (2)编写一个java程序,计算半径为3.0的圆周长和面积并输出结果。 (3)求a+aa+aaa+...+a...a(n个)的和,其中a为1~9之间的整数。例如,当a=3、n=4时,求3+33+333+3333的和。 (4)给定一个正整数m,统计其位数,分别打印每一位数字,再按照逆序打印出各位数字。(5)用Scanner类方法输入三角形三边求三角形面积。 实验要求 (1)JDK的安装及配置。 (2)在DOS及eclipse下编辑、编译运行第一个java程序:hello world。 (3)求圆周长和面积用方法实现,主函数调用。 (4)从命令行输入1~9之间的整数a,当所求的和大与106时,输出相应的a值及所求的和值。 (5)用Scanner类的方法输入正整数m,m的值不应该超过99999,否则给出错误信息。应引入包:import java.util.Scanner,然后在需要的方法中实例化对象:Scanner sc = new Scanner(System.in),最后调用对象的next方法,如int n=nextInt(),接受整数。 实验二数组 实验目的 (1)掌握数组的定义和使用方法。 (2)熟悉数组的排序、查找的方法。 (3)巩固循环的使用。 实验内容 (1)使用for循环,将二维数组的行与列互换,即完成矩阵的转置。 (2)编写数组的排序程序。 (3)编写杨辉三角。 实验要求 (1)编写一个界面1 选择排序2 冒泡排序3插入排序4 快速排序5 退出 当选择1、2、3、4、5的时候完成相应的功能。 (2)杨辉三角形状为等腰三角形 实验三字符串 实验目的 (1)掌握正则表达式的使用。 (2)熟悉java的字符串。

Java架构学习【JVM与性能优化知识点整理】编写高效优雅Java程序

面向对象 构造器参数太多怎么办? 用builder模式,用在 1、5个或者5个以上的成员变量 2、参数不多,但是在未来,参数会增加 Builder模式: 属于对象的创建模式,一般有 1.抽象建造者:一般来说是个接口,包含1)建造方法,建造部件的方法(不止一 个),2)返回产品的方法 2.具体建造者 3.导演者,调用具体的建造者,创建产品对象 4.产品,需要建造的复杂对象 对于客户端,创建导演者和具体建造者,并把具体建造者交给导演者,然后由客户端通知导演者操纵建造者进行产品的创建。 在实际的应用过程中,有时会省略抽象建造者和导演者。 不需要实例化的类应该构造器私有 如,一些工具类提供的都是静态方法,这些类是不应该提供具体的实例的。可以参考JDK 中的Arrays。 不要创建不必要的对象 1.避免无意中创建的对象,如自动装箱 2.可以在类的多个实例之间重用的成员变量,尽量使用static。

但是,要记住,是不要创建不必要的对象,而不是不要创建对象。 对象池要谨慎使用,除非创建的对象是非常昂贵的操作,如数据库的连接,巨型对象等等。 避免使用终结方法 finalizer方法,jdk不能保证何时执行,也不能保证一定会执行。如果有确实要释放的资源应该用try/finally。 使类和成员的可访问性最小化 编写程序和设计架构,最重要的目标之一就是模块之间的解耦。使类和成员的可访问性最小化无疑是有效的途径之一。 使可变性最小化 尽量使类不可变,不可变的类比可变的类更加易于设计、实现和使用,而且更不容易出错,更安全。 常用的手段: 不提供任何可以修改对象状态的方法; 使所有的域都是final的。 使所有的域都是私有的。 使用写时复制机制。带来的问题:会导致系统产生大量的对象,而且性能有一定的影响,需要在使用过程中小心权衡。 复合优先于继承 继承容易破坏封装性,而且会使子类的实现依赖于父类。 复合则是在类中增加一个私有域,引用类的一个实例,这样的话就避免了依赖类的具体实现。 只有在子类确实是父类的一个子类型时,才比较适合用继承。 接口优于抽象类 java是个单继承的,但是类允许实现多个接口。

Java开源架构技术学习重点(部分答案版)(1)

第一章 Strut2框架技术的入门 1、Struts2的是怎么产生的? Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并,全新的Struts2框架。其全新的Struts2的体系结构与Struts1的体系结构的差别巨大。Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与Servlet API完全脱离开,所以Struts 2可以理解为WebWork的更新产品。因此Struts2和Struts 1有着太大的变化,但是相对于WebWork,Struts2只有很小的变化。 2、Struts2的设计模式是什么?采用这种设计模式有什么好处? MVC模式 MVC模式对于Web应用的开发无疑是一种非常先进的设计思想,无论选择 哪种语言,无论应用多复杂,它都能为理解分析应用模型提供最基本的分析方法, 为构造产品提供清晰的设计框架,为软件工程提供规范的依据。 1. 模型(Model) Model 部分包括业务逻辑层和数据库访问层。在Java Web 应用程序中,业务逻辑层一般由JavaBean或EJB构建。Model 部分就是业务流程或状态的处理以及业务规则的制定。业务流程的处理过程对其他层来说是黑箱操作,模型接受视图请求的数据,并返回最终的处理结果。业务模型的设计可以说是MVC最主要的组件。MVC并没有提供模型的设计方法,而只提供给用户应该组织管理这些模型,以便于模型的重构和提高重用性。 2. 视图(View) 在Java Web 应用程序中,View 部分一般用JSP 和HTML 构建,也可以是XHTML、XML、Applet和JavaScript。客户在View 部分提交请求,在业务逻辑层处理后,

java类加载机制

Java类加载机制(一) 译:ayi 第一次翻译,翻译得不是很好,请多多指点 我的邮箱:nonopo12345@https://www.sodocs.net/doc/4d5492140.html, 原文:https://www.sodocs.net/doc/4d5492140.html,/pub/a/onjava/2005/01/26/classloading.html 类加载是java特性的一个重量级的组成部分。尽管,java中“advanced topics”的发展,使java的类加载机制的地位有所下降。但每位编程着都应该知道这部分的工作机制,以及怎样去更好与其配合。这可以使我们节省很多时间,二不必要浪费在调试ClassNotFoundException, ClassCastException, 等。 这篇文章将从最基本的开始,比如代码和数据的关系和区别,以及他们怎么样关系起来形成一个实例或者对象。然后将会说到,java中怎样通过类加载器把代码加载到JVM中,以及java中实现的主要的几种类型的类加载器。然后在这篇文章中我们将会了解到java类加载机制的内幕,我们将使用最基本的代码来描述,这些代码执行与类加载器之后,但在加载一个类之前。在接下来的部分将使用一些例子来强调,对于开发者继承和开发自己的类加载器的必要性。接着将告诉你们怎样编写自己的类加载器,以及怎样使用它们去创建一个一般的能加载包括远程客户端辅助代码的类加载器引擎,以及怎样把它在JVM中定义,实例化,然后执行。习惯上,把J2EE-specific components 中说明的作为java类加载的规范,这篇文章正是从这本手册总结来的。 类和数据 一个类代表一段要执行的代码,然而数据则代表与这些代码相关联的某种状态。状态可以改变,代码不能改变。我们把一种特定状态与一个类关联起来时,就得到了这个类的一个实例。所以同一个类的不同实例有不同的状态,但都参照相同的代码。在java中,一个类通常它的代码就包含在一个 .class 文件中,虽然其中也包括异常。然而,在java运行时,每个类都会构造一个超类对象(first-class object),它们其实是https://www.sodocs.net/doc/4d5492140.html,ng.Class的实例。不论何时编译一个java文件,编译器都会在编译后的字节码中嵌入一个public, static, final型的字段class,这个字段表示的就是一个https://www.sodocs.net/doc/4d5492140.html,ng.Class型的实例。因为它是public类型的,我们可以通过标识符来访问它,像这样: https://www.sodocs.net/doc/4d5492140.html,ng.Class klass = Myclass.class; 只要一个类被加载到JVM,相同的类(强调:相同的类)将不会被重复加载。这将产生一个问题,什么才是相同的类?一个对象有一种特定状态和标识,对象总是与它所属类联系在一起,与这种状况相似,一个被加载到JVM中类也有特定的标识,接下来我们就阐述: 在java中,一个类通过认证的类全名来唯一标识。认证的类全名是由包名和类名两部分组

java 使用tomcat类加载器加载类,找不到加载器

The Tomcat installation directory is not valid. It is missing expected file or folder common/i18n. eclipse 配置使用apache-tomcat-5.5.34时,提示如上。 换了个apache-tomcat-5.5.29 的版本。 但是加载启动项目时,出现类加载器无法加载类。因为apache-tomcat-5.5.29 版本中缺少类加载器的类。 Caused by: https://www.sodocs.net/doc/4d5492140.html,ng.ClassNotFoundException: org.apache.catalina.loader.WebappLoader 在\apache-tomcat-5.5.29\conf\Catalina\localhost 下会有项目配置的比如project.xml运行生成。这样启动的时候才会把项目加载上。 在eclipse\plugins\com.sysdeo.eclipse.tomcat_3.2.1 找到DevLoader.zip 文件夹。 1、在eclipse目录下,找到DevLoader包,位于 eclipse\plugins\com.sysdeo.eclipse.tomcat_3.2.1的DevLoader.zip 2、复制DevLoader.zip到Tomcat apache-tomcat-5.5.29 的\lib下。 3、解压缩DevLoader.zip在当前文件夹,然后把\DevLoader 下的org文件夹全部copy到\apache-tomcat-5.5.29\server\classes下。必须是server\classes 下,而不能是apache-tomcat-5.5.29\common\classes 下。

JAVA笔试题1

知识点: (1) 1.面向对象的软件开发有哪些优点? (1) 2.什么叫对象?什么叫类?类和对象有什么关系。 (1) 3.什么是包?把一个类放在包里有什么作用? (2) 4.作用域public、private、protected以及不写时(default)有什么区别? (2) 5.什么是方法?方法的结构是怎样的?设计方法应考虑哪些因素? (2) 6.什么是方法的覆盖?与方法的重载有何不同?方法的覆盖与属性的隐藏有何不同? (3) 7.什么是成员变量、局部变量、类变量和实例变量? (3) 8.什么是继承?什么是父类?什么是子类?继承的特性可给面向对象编程带来什么好处? (3) 9.什么是多态?面向对象程序设计为什么要引入多态的特性? (4) 10.“子类的域和方法的数目一定大于等于父类的域和方法的数目”,这种说法是否正确?为什么? (4) 11.父类对象与子类对象相互转化的条件是什么?如何实现它们的相互转化? (4) 12.接口与抽象类有哪些异同点? (4) 13.区分接口与抽象类分别在什么场合使用? (5) 14.一个类如何实现接口?实现某接口的类是否一定要重载该接口中的所有抽象方法? (5) 15.进程与线程的区别与联系? (5) 编程: (6) 1.统计一个字符串中出现某个字母的次数(注意区分大小写)。 (6) 2.编写一个程序,在主类中创建和调用方法sumf(),方法sumf()的功能是进行两个浮点数的加法运算。6知识点: 1.面向对象的软件开发有哪些优点? 面向对象设计是一种把面向对象的思想应用于软件开发过程中,指导开发活动的系统方法,是建立在“对象”概念基础上的方法学。所谓面向对象就是基于对象概念,以对象为中心,以类和继承为构造机制,来认识、理解、刻画客观世界和设计、构建相应的软件系统。 从面向过程到面向对象是程序设计技术的一个飞跃。人们之所以要采用面向对象的程序设计技术,其目的在于:按照与人类习惯思维方法一致的原则开发系统;提高代码的可重用性(或者称为复用性);提升程序的开发与运行效率;提高程序的可靠性与可维护性;提高程序的可扩展性;增强程序的可控制性。总之,面向对象的程序设计,能够有效分解、降低问题的难度与复杂性,提高整个求解过程的可控制性、可监视性和可维护性,从而获得较高的开发效率与可靠效果。 2.什么叫对象?什么叫类?类和对象有什么关系。 对象(Object)是一个应用系统中用来描述客观事物的实体,是具有特定属性(数据)和行为(方法)的基本运行单位,是类的一个特定状态下的实例。对象是一件事、一个实体、一个名词、一个可以想象为有自己的标识的任何东西。对象是类的实例化。概括来说:万物皆对象。对象具有状态,一个对象用数据值来描述它的状态。 类(Class)是Java代码的基本组织模块,是用以描述一组具有共同属性和行为的对象的基本原型,是对这组对象的概括、归纳与抽象表达。类是对象的模板,它定义了本类对象所应拥有的状态属性集及操作这组属性的行为方法集。是对一组有相同数据和相同操作的对象的定义,一个类所包含的方法和数据描述一组对象的共同属性和行为。

JAVA类与对象的创建

试验四 类和对象的创建 一.类的定义 类是组成Java程序的基本要素。类封装了一类对象的状态和方法。类用来定义对象的模板。 类的实现包括两部分:类声明和类体。基本格式为: 修饰符class类名[extends父类名]{ /*类体的内容*/ } 1.类声明 以下是一个类声明的例子。 class People成为类声明,People是类名。习惯上,类名的第一个字母大写,但这不是必须的。类的名字不能是Java中的关键字,要符合标识符规定,即类的名字可以由字母、下划线、数字或美元符号组成,并且第一个字母不能是数字。但给类命名时,最好遵守下列规则: (1)如果类名使用拉丁字母,那么名字的首写字母使用大写字母,如Hello、Time、People等。 (2)类名最好容易识别,当类名由几个“单词”复合而成时,每个单词的首写字母使用大写,如BeijingTi me、AmericanGame、HelloChina等。 2.类体 编写类的目的是为了描述一类事物共有的属性和功能,描述过程由类体来实现。类声明之后的一对大括号“{”、“}”以及它们之间的内容称为类体,大括号之间的内容称为类体的内容。 类体的内容由两部分构成:一部分是变量的定义,用来刻画属性;另一部分是方法的定义,用来刻画功能。 下面是一个类名为“Trapezia”的类,类体内容的变量定义部分定义了4个float类型变量:top、bottom、h igh和laderArea,方法定义部分定义了两个方法:“getArea”和“setHigh”。

二.对象 1.对象的创建 创建一个对象包括对象的声明和为对象分配内存两个步骤。 (1)对象的声明。 一般格式为: 类的名字对象名字; 如: 这里People是类的名字,zhubajie是我们声明的对象的名字。 (2)为声明的对象分配内存。 使用new运算符和类的构造方法为声明的对象分配内存,如果类中没有构造方法,系统会调用默认的构造方法(你一定还记得构造方法的名字必须和类名相同这一规定),如: 例1: 「注」如果类里定义了一个或多个构造方法,那么Java不提供默认的构造方法。 2.对象的使用 对象不仅可以改变自己变量的状态,而且还拥有了使用创建它的那个类中的方法的能力,对象通过使用这些方法可以产生一定的行为。 通过使用运算符“.”,对象可以实现对自己的变量访问和方法的调用。 例2:

Java技术详细介绍

Java技术详细介绍 Java的历史:微处理的革命最重要的也许就是它使全世界很快拥有3亿台个人计算机成为可能。个人计算机对个人以及对企业的生产管理方都产生了深刻的影响。 本文内容由天搜科技整理提供 Java程序由类组成,类则由方法构成。方法用来执行任务,并在完成任务时返回信息。用户可以编写类和方法,并由此建立一个Java程序,在编写过程中他们还应尽量充分利用Java类库中已存在的丰富的类和方法。类库就是Java API(Applications Programming Interface,应用程序接口).因此学习Java 语言实际上包括两个方面:一方面是学习用Java语言编写自己所需的类和方法,另一方面是学习如何利用Java类库中的类和方法。 Java核心技术有哪四个? 1.Java虚拟机 2.类装载器的体系结构 3.Java class文件 4.Java API 下面分别介绍Java四个核心技术的应用: Java虚拟机 Java虚拟机处于机器和编译程序之间,在任何平台上都提供给编译程序一个共同的接口。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行,Java应用程序能够在运行时决定需要安装的类,并且将被不同的类装载器装载的类存放在不同的命名空间。 类装载器的体系结构 一个Java应用程序可以使用两种类装载器:"启动(bootstrap)"类装载器和用户定义的类装载器。启动类装载器(这是系统中唯一的)是Java虚拟机实现的一部分。启动类装载器通常使用某种默认方式从本地磁盘中装载类,包括Java API类(启动类装载器也被称为原始类装载器、系统类装载器或者默认类装载器)。

java找不到或无法加载主类XXX

java 找不到或无法加载主类XXX 使用命令行运行Java文件时,有时会提示错误“找不到或无法加载主类XXX”,这里涉及到三个可能的原因: (1)java是环境变量JA V A_HOME、CLASSPATH、Path没有正确配置。 (2)命令行所在目录和Java文件所在目录不同。 (3)定义了包名的类,需要特殊处理 1.原因(1)的解决方法 在网上有很多配置Java环境变量的解决方法,原理都是一样的,这里就不再详述;2.原因(2)的解决方法 当命令行所在目录和Java文件所在目录不同时,一定要给出Java文件的完整路径名称! 例如:在D盘根目录下编写一个Hello.java文件,那么Hello.java的完整路径名称为D:\Hello.java。 1.打开命令行,执行javac Hello.java,报错“javac:找不到文件:Hello.java”,这是因为命令行的目录为C:\Users\Administrator,与Hello.java的目录不同,命令行javac从它的当前目录找不到Hello.java。 2.将命令行切换到D盘根目录下,执行javac Hello.java,此时没有报错,说明编译成功,然后再执行java Hello,程序成功运行。当Java文件和命令行在同一目录下时,直接输入Java文件名称,即可成功运行。 3.那么如何在不同的目录下成功运行Java文件呢?解决方法:当Java文件和命令行在不同目录时,编译时输入Java文件的完整路径名称;执行时,因为classpath指定了Java 类的执行路径,Java类只有包含在classpath路径下才能被java命令识别,有如下两种方法:(1)通过set classpath命令设置Java类的临时执行路径 (2)在系统环境变量classpath后面加上;d:;,表示搜索Java类时会从d盘目录搜索

深入探讨、理解Java的CLASSPATH

深入探讨、理解Java的CLASSPATH 来源: 本站原创 从表面上看,Java的classpath(类路径)很简单,但一直以来它都是一个产生问题和混乱的根源。本文介绍classpath的基本知识、可能产生的问题,并提供了一个简单的classpath管理工具。 和Java类路径(classpath)打交道的过程中,开发者偶尔会遇到麻烦。这是因为,类装载器实际装入的是哪一个类有时并不显而易见,当应用程序的classpath 包含大量的类和目录时,情况尤其严重。本文将提供一个工具,它能够显示出被装入类文件的绝对路径名。 一、Classpath基础 Java虚拟机(JVM)借助类装载器装入应用程序使用的类,具体装入哪些类根据当时的需要决定。CLASSPATH环境变量告诉类装载器到哪里去寻找第三方提供的类和用户定义的类。另外,你也可以使用JVM命令行参数-classpath分别为应用程序指定类路径,在-classpath中指定的类路径覆盖CLASSPATH环境变量中指定的值。 类路径中的内容可以是:文件的目录(包含不在包里面的类),包的根目录(包含已打包的类),包含类的档案文件(比如.zip文件或者.jar文件)。在Unix家族的系统上,类路径的各个项目由冒号分隔,在MS Windows系统上,它们由分号分隔。 类装载器以委托层次的形式组织,每一个类装载器有一个父类装载器。当一个类装载器被要求装载某个类时,它在尝试自己寻找类之前会把请求先委托给它的父类装载器。系统类装载器,即由安装在系统上的JDK或JRE提供的默认类装载器,通过CLASSPATH环境变量或者-classpath这个JVM命令行参数装入第三方提供的类或者用户定义的类。系统类装载器委托扩展类装载器装入使用Java Extension机制的类。扩展类装载器委托自举类装载器(bootstrap class loader)装入核心JDK类。 你可以自己开发特殊的类装载器,定制JVM如何动态地装入类。例如,大多数Servlet引擎使用定制的类装载器,动态地装入那些在classpath指定的目录内发生变化的类。

2011JAVA程序设计A卷试卷(含答案)

华南农业大学期末考试试卷(A 卷) 2011-2012学年第 1 学期 考试科目: Java 程序设计 考试类型:(闭卷)考试 考试时间: 120 分钟 学号 姓名 年级专业 一、单项选择题(本大题共 25 小题,每小题 2 分,共 50 分) 1. 下列有关Java 程序的说法中正确的是___D_____: A. 由于Java 程序是解释执行的,所以执行前不需要进行编译 B. 一个.java 源程序编译后将产生一个.class 的字节码文件 C. 安装了JDK 后,安装程序会自动配置系统的环境变量path 和classpath D. 面向对象的解释型高级编程语言 2. 下列为合法变量名的是__C______。 A. false B. 0xabc C. iStudent D. cow ?s 3. 对于构造方法,下列叙述不正确的是__C______。 A. 构造方法是类的一种特殊函数,它的方法名必须与类名相同 B. 构造方法没有返回值类型 C. 子类不但可以继承父类的无参构造函数,也可以继承父类的有参构造函数。 D. 子类在创建构造方法是必须把调用父类构造方法放在第一条语句 4. 下列关于接口的说明正确的是__D______。 A. 接口和抽象类是同一回事 B. 一个类不可以实现多个接口 C. 接口间不能有继承关系 D. 实现一个接口必须实现接口的所有方法 5. Java 为移动设备提供的平台是__A______。 A . J2ME B .J2SE C .J2EE D .JDK5.0 6. 下列语句中,属于多分支语句的是__B______。 A .if 语句 B .switch 语句 C .do while 语句 D .for 语句

相关主题