Posts Tagged ‘ Java ’


几个GAE/J中的Transaction的概念

Transaction是在计算机很多领域里都需要的,最基础的知识相信大家都懂,那就是一个或一组操作,要么都成功,一旦有任何一个失败则所有的都失败,要回滚到原始位置。这个概念在GAE/J中一样。在datastore中,所有的写操作都被认为是atomic的,任何尝试着创建,更新或者删除实体的操作要么成功,要么就失败。一个操作有可能因为很多原因而失败,比如当许多用户同时尝试去修改这个值,或者该操作超过了其允许的配额值,当然也有可能是datastore的内部错误造成。在任何情况下的出错,都会使得所有操作就像没有发生一样而回滚。

实体组在GAE/J中是一个十分重要的概念。最开始的时候,我也是什么都想着往关系数据库上套,结果发现一来出现很多外键上的问题,二来出现很多包含关系的问题。个人认为,对于datastore而言,实体组是必须十分熟悉的内容。实体组之间的关系是以父子来称呼的,比如一个实体是国家,另一个实体是省份,那么显然一个国家会有很多省份而一个省份属于一个国家,于是我们可以将国家视作一个父实体,则通过每一个子实体都可以得到父实体也就是这个国家。当实体之间有了父子关系的时候,我们可以将他们视为在一个实体组中。对于一个没有父实体的实体而言,它就是一个根实体。一个实体的父实体是在该实体创建时就设定好了而不可以更改的。 处在同一个根实体下的所有实体都可以看作属于同一个实体组,被存在同一个datastore节点上。

那么实体组和Transaction的关系在哪呢?一个单一的Transaction只能对一个实体组进行操作,或者将另一个实体加入到该实体组中去。无论是查询,更新还是删除实体,如果一旦涉及到多个实体组,就会报错。同样的道理,在一个单一的Transaction中,不能涉及到多个实体有多个根节点的,因为一个根节点就等于代表了一个实体组。

如果在任何的tx.begin()和tx.commit()之间,有多个进程同时调用同样的实体组,那么JDO就会抛出JDODataStoreException或者JDOException异常,这是由java.util.ConcurrentModificationException造成的。解决的办法可以通过建立一个循环,多次进行重试来保证每个进程都能够成功。如果在一个进程之间,更新多个实体组,则会报JDOFatalUserException的异常。

Datastore的隔离级别在Transaction之外是最接近于READ_COMMITTED,在Transaction之内是SERIALIZABLE,特别指出是一种Snapshot Isolation。

我们在更新一个实体的部分值时需要使用Transaction,原因是我们在修改的同时可能也会有其他的进程在修改。另外一个需要使用的地方实在更新或者创建Key的时候,道理与上者类似,以避免多进程的同时操作。最后一个需要使用的地方是多个读操作发生的时候,为了保持数据的一致性而使用Transaction。

一个Transaction应该很快的进行,来减少发生冲突的机会从而减少重试的次数。尽可能的在Transaction之外把数据准备好,然后在Transaction内部进行执行需要保持数据一致性的操作。应该在Transaction之内来准备Key,和使用Key来获得实体。将datanucleus.appengine.autoCreateDatastoreTxns设置为false,则所有的Transaction都被禁用了。该文件的位置位于war/WEB-INF/classes/META-INF。

如果你的代码是以前根据关系数据库编写的,里面的Transaction极有可能是基于全局的,于是在GAE/J下必然会报错。一个好的解决办法是,首先禁用掉GAE/J中的所有的Transaction,然后一步步的操作来解决这个Transaction的问题。

在GAE/J上玩Web服务

Web服务是GAE的主要交流工具,其中的核心部分就是RESTlet。但是目前来说,市场上还有很多很多的Web服务是只提供SOAP服务的,比如我现在正在解决的关于调用GoGrid CDN的API,在CDN这一块,他们只提供SOAP。但是想要在GAE/J上调用SOAP简直就是不可能。因为对很多类的限制,JAX-WS等一系列的传统方式,都无法在GAE/J上运行。

有一个force.com的网站,提供了一个婉转的办法。他们提供了一个WSC的类,可以将下载下来的WSDL文件,自动生成一个JAR包其中包含所需要链接的connector和类,然后在这个类的基础之上来操纵web服务。有兴趣的同志们可以直接去看看。

但是我的问题更严重,因为GoGrid CDN的SOAP API是基于.Net的,而我是用的Java,这又是一条横岗。在GAE的Issue里已经提交了这个问题,希望Google早日支持这个库,不然的话我可不想从JAXB开始手写调用Web服务。

先暂且把这个Task放一放,Azure这个服务商提供的API是支持REST的,这样总可以解决一些问题了。

GAE-Java一些小的注意的地方

1. GAE的Eclipse在引用本地的library的时候,不会自动将其复制到WEB-INF/lib下去,结果就是如果选择直接上传到SERVER,程序会报找不到类的错。目前的开发人员正在改正这个问题,可能会在下一版得到解决。目前的最好解决办法,是首先将需要的类复制到WEB-INF/lib下,然后再引用。

2. 如果希望在本地调试程序的时候,可以查看datastore和queuetask的信息,可以通过http://localhost:8080/_ah/admin进入。里面提供了手动调控task的功能。

GAE-Java中JDO部分的KEY

发现有些部分的文档,看一次是绝对不可能看懂的。必须要过两天回头再来看,才发现一些端倪。比如这里要说的是JDO中类的KEY。

习惯了RDBMS开发的同志们,都会想到这还不是一个很简单的问题,无非就是选择一个唯一的可以代表该实例的作为ID就可以了。特别是目前开发中,大多数人喜欢用系统自动生成的ID序列来构造key。然而在GAE所支持的JDO中,我们会发现文档中提供了四种方式来实现KEY,不同的方法适用于不同的场合。

1. Long变量
这就是上面所说的最常见的一种,由系统自动生成的长整型变量来表示KEY。注意,这种选择只适用于那些没有父类的类。什么叫没有父类的类呢?例如有两个类,People和ContactInfo,在People类中,有一个属性就是ContantInfo,那么在这种情况下,People类就是有父类的类,而ContactInfo就是没有父类的类。

2.Unencoded String
和上面的Long变量一样,这个KEY的实现同样只适用于那些没有父类的类。但是在这里,它的值是由程序指定的,在这里使用的是没有编码的String类型。

3.Key
Key类是GAE-Java中专门提供的作为KEY的类型,它包含了可能有的父类信息以及一个系统自动分配或者程序指定的ID。也就是说,它是在包括父类信息的前提之下,选择以上两种选择之一来共同完成Key的任务。用KeyFactory的createKey()方法即可完成这一功能。

4.Key作为编码String
这种类型和Key类型是很相似的,只是将Key转化为来String来进行操作,方便使用。Key和encoded String之间可以用KeyFactory的keyToString()和stringToKey()方法互相转换。因为是String类型,所以如果想要得到ID的其中的内容,可以在声明了Key之后,附加声明其String或者Long类型的ID。@Extension(vendorName=”datanucleus”, key=”gae.pk-name或者gae.pk-id”,value=”true”)。同样,name类型的值是可以更改的,但是id类型的是自动生成无法变化的。

如何生成Key?
1. 对于没有父类的类型,我们可以使用createKey()静态方法来实现。它的参数包括createKey(String kind, String name)。其中kind就是指的要创造的类的类型,一般用Class.class.getSimpleName()来实现。第二个参数取决于使用的是自动生成的Long变量还是程序指定的String变量而定。

2,对于有父类的类型,我们同样可以使用createKey()的另一个重载方法来实现createKey(Key parent, String kind, long id)。在这里,我们首先实现父类的Key,然后将其包括在方法参数内即可。
不过在教程上,提供了另外一种方法来是实现,使用的是KeyFactory.Builder类。在这里首先通过构造函数,实现一个Key,然后不断调用addChild()方法,把子类附加上,最后通过getKey()方法得到Key。这种方法的好处在于可以产生一个链状的Builder,更加方便使用。比如在我们上面的People和ContactInfo例子之中,首先我们生成ContactInfo的Key,然后下一步附加上People的Key即可。

如何通过Key得到对象
一个很简单的方法,就是pm.getObjectById(class, key/id);如果你有的是key,第二个参数就用key;如果有的是id,第二个参数就用id。同时,你可以选择使用非类里面声明的类型,来作为第二个参数。比如在类里面使用的是Key,你可以通过调用name的String来获得。唯一的例外是String和Long之间的互相调用是不允许的。

Restlet2.0初尝试

今天一直在研究Restlet的API,大约应该是在2007年起,1.0版就已经问世了。但是由于这个概念在当时还是比较新的,所以1.0版本的普及率特别是在国内的普及率还是很低的。百度一下就会发现研究的人的确不多。现在RESTful的概念是越吵越热,且不说它到底好坏与否,但是引起这么多人的关注,就说明了它有其出色的地方。

在网站上看到的Stable的版本是1.6,于是保守地先从这个版本试起,结果发现无论如何添加Connector,都会报找不到Protocol的错,恰好这时James过来看看我,于是在他的建议之下,尝试了2.0m5版本。至少在最简单的程序部分,能够很正常的运行。

下面把一些很简单的步骤作个罗列:

在我看来,完成一个全部由Restlet构造的程序,一共需要四个部分。首先便是资源文件,对于REST概念比较熟悉的同学都应该明白,Resource在REST中是根本中的根本。于是,我们首先定义一个十分简单的Resource,为了简单起见,我们只给它附加两个功能,GET和POST。其中GET方法,很简单的返回一个String,POST方法是将得到的两个数进行相加返回结果字符串。

public class TestResource extends ServerResource{

	@Get
	public String getResultGet(){
		return "This is my test!";
	}

	@Post
	public String getResultPost(Representation entity){
		Form form = new Form(entity);
		String first = form.getFirstValue("first");
		String second = form.getFirstValue("second");
		int a = Integer.parseInt(first);
		int b = Integer.parseInt(second);

		return "The result of "+a+"+"+b+"is "+(a+b);
	}
}

在这一步结束之后,我们需要把资源放到一给应用(Application)中去,同时要提供访问它的路径。我个人心目中就把这个部分成为RouterPath,因为这两个单词也是这个文件中最多见的。

public class RouterPath extends Application{
        @Override
	public Restlet createInboundRoot() {
		// TODO Auto-generated method stub
		Router router = new Router(getContext());

		router.attach("/greeting",TestResource.class);

		return router;
	}
}

在这一给类中,它继承来Application这个类,目的就可以看成是提供了一个程序环境,在这个大的环境之下,你输入的方法,就将被引导到某个类之中去。比如在这里,所有的以greeting结尾的请求,都会被引导到我们刚才所创建的类里面去。

有来这样两样以后,我们只需要启动这个资源让它开始服务就可以了。我把这个类命名为StartResource类。

public class StartResource {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			// Create a new Component.
			Component component = new Component();  

			// Add a new HTTP server listening on port 8182.
			component.getServers().add(Protocol.HTTP, 8182);  

			// Attach the sample application.
			component.getDefaultHost().attach(new RouterPath());  

			// Start the component.
			component.start();
		}catch (Exception e) {
			// Something is wrong.
			e.printStackTrace();
		}
	}
}

我们可以很清楚的看到,这个类启动来在端口8182上的HTTP协议,然后将前面的Application附加在来这个端口上。然后服务就开始启动,等待他人的使用了。

现在我们就可以直接在浏览器中输入这个URL,来调用GET方法了。在这里,我们输入http://localhost:8182/greeting,屏幕上就出现来This is my test!字样。当然浏览器是不支持POST,PUT和DELETE直接在地址栏中的,我们可以用一个简单的JSP界面做一给POST框体,也可以像如下的类用Restlet提供的Client来完成请求。

public class Client {
	public static void main(String[] args) {
		int a=4;
		int b=9;
		ClientResource client = new ClientResource("http://localhost:8182/greeting");
		try {
			System.out.println(client.get().getText());
			Form form = new Form();
			form.add("first", a+"");
			form.add("second",b+"");
			System.out.println(client.post(form.getWebRepresentation()).getText());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

很容易看到,GET方法是可以直接用client得到的,而POST方法需要传入一个参数,也就是Resource端所需要的参数。用类似的办法,我们同样可以完成PUT和DELETE。

以上的这个例子实际上非常基础,但是至少展现来Restlet的一些特点,首先它是一个很完善的既支持客户端又支持服务端的API;其次它的结构也十分清晰,只要很好的划分了不同的请求,就能很好地看清楚之间的逻辑;最后就是API的封装还是很简洁的,尤其是在客户端调用这一块。

明天开始继续深入研究,更多的Tutorial可以参见http://www.restlet.org/documentation/2.0/tutorial

RESTful Web Services Developer’s Guide

Chapter 1
Introduction to RESTful Web Services and Jersey
1. What are RESTful Web Services?
RESTful - REpresentational State Transfer is an architectural style that specifies constraints, such as the uniform interface, that if applied to a web service induce desirable properties, such as performance, scalability, and modifiability, that enable services to work best on the Web.
Data and functionality are considered resources, and these resources are accessed using URIs (Unifomr Resource Identifiers), typically links on the web.
The REST architectural style constrains an architecture to a client-server architecture, and is designed to use a stateless communication protocol, typically HTTP. Clients and servers exchange representations of resources using a standardized interface and protocol.
RESTful web services typically map the four main HTTP methods to the operations they perform: create, retrieve, update and delete.
GET — Get a resource;    POST — Create a resource and other operations, as it has no defined semantics;    PUT — Create or update a resource;    DELETE — delete a resource

2. How does Jersey fit in?
Jersey is Sun’s production quality reference implementation for JSR 311: JAX-RS: The Java API for RESTful Web Services. Jersey implements support for the annotations defined in JSR-311, making it easy for developers to build RESTful web services with Java and Java JVM. Jersey also adds additional features not specified by the JSR.

Chapter 3
Creating a RESTful resource class
Root resource classes are POJOs that are either annotated with @Path or have at least one method annotated with @Path or a request method designator such as @GET, @PUT, @POST,  or @DELETE.
Resource methods are methods of a resource class annotated with a request method designator.

1. Developing RESTful web services with Jersey
Developers decorate Java programming language class files with HTTP-specific annotations to define resources and the actions that can be performed on those resources. Jersey annotations are runtime annotations, therefore, runtime reflection will generate the helper classes and artifacts for the resource, and then the collection of classes and artifacts will be built into a WAR. The resources are exposed to clients by deploying the WAR to a Java EE or web server.

@Path annotation’s value is a relative URI path. What makes JAX-RS so useful is that you can embed variables in the URIs. URI path templates are URIs with variables embedded within the URI syntax.
@GET annotation is a request method designator, along with @POST, @PUT, @DELETE, and @HEAD, that is defined by JAX-RS, and which correspond to the similarly named HTTP methods.
@Produces annotation is used to specify the MIME media types of representations a resource can produce and send back to the client.
@Consumes annotation is used to specify the MIME media types of representations a resource can consume that were sent by the client.

2. What are some of the annotations defined by JAX-RS?

Annotations Description
@Path The @Path annotation’s value is a relative URI path indicating where the Java class will be hosted, for example, /helloworld. You can also embed variables in the URIs to make a URI path template. For example, you could ask for the name of a user, and pass it to the application as a variable in the URI, like this, /helloworld/{username}.
@GET The @GET annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP GET requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@POST The @POST annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP POST requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@PUT The @PUT annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP PUT requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@DELETE The @DELETE annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP DELETE requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@HEAD The @HEAD annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP HEAD requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@PathParam The @PathParam annotation is a type of parameter that you can extract for use in your resource class. URI path parameters are extracted from the request URI, and the parameter names correspond to the URI path template variable names specified in the @Path class-level annotation.
@QueryParam The @QueryParam annotation is a type of parameter that you can extract for use in your resource class. Query parameters are extracted from the request URI query parameters.
@Consumes The @Consumes annotation is used to specify the MIME media types of representations a resource can consume that were sent by the client.
@Produces The @Produces annotation is used to specify the MIME media type of representations a resource can produce and send back to the client, for example, “text/plain”.
@Provider The @Provider annotation is used for anything that is of interest to the JAX-RS runtime, such as MessageBodyReader and MessageBodyWriter. For HTTP requests, the MessageBodyReader is used to map an HTTP request entity body to method parameters. On the response side, a return value is mapped to an HTTP response entity body using MessageBodyWriter. If the application needs to supply additional metadata, such as HTTP headers or a different status code, a method can return a Response that wraps the entity, and which can be built using Response.ResponseBuilder.

3. The @Path annotation and URI path templates.
The @Path annotation identifies the URI path template to which the resource responds, and is specified at the class level of a resource. URI path templates are URIs with variables embedded within the URI syntax. These variables are substituted at runtime in order for a resource to respond to a request based on the substituted URI. Variables are denoted by curly braces.
@Path (”/users/{username}”)
if user entered user name as Galileo, the web service will respond to the following URL: http://example.com/users/Galileo

The @PathParam annotation may be used on the method parameter of a request method to obtain the value of the username variable.
public class UserResource{
@GET
@Produces(”text/xml”)
public String getUser(@PathParam(”username”) String userName){

}
}

By default, there is no difference between @Path value begin or not begin with a forward slash(/) and end with a forward slash(/) as well. However, Jersey has a redirection mechanism, if enabled, will perform redirection.

4. Responding to HTTP Resources
The behavior of a resource is determined by the HTTP methods (typically GET, POST, PUT, and DELETE)  to which the resource is responding.
By default, the JAX-RS runtime will automatically support the methods HEAD and OPTIONS if not explicitly implemented. For HEAD, the runtime will invoke the implemented GET method (if present) and ignore the response entity (if set). For OPTIONS, the Allow response header will be set to the set of HTTP methods support by the resources. In addition Jersey will return a WADL document describing the resource.
Method decorated with request method designators must return void, a Java programming language type, or a javax.ws.rs.core.Response object. Multiple parameters may be extracted from the URI using the PathParam or QueryParam annotations. Conversion between Java types and entity body is the responsibility of an entity provider, such as MessageBodyReader or MessageBodyWriter. Methods that need to provide additional metadata with a response should return an instance of Response. The ResponseBuilder class provides a convenient way to create a Response instance using a builder pattern. The HTTP PUT and POST methods expect an HTTP request body, so you should use a MessageBodyReader for methods that respond to PUT and POST requests.

5. Using entity providers to map HTTP response and request entity bodies.
Entity providers supply mapping services between representations and their associated Java types.
The following list contains the standard types that are supported automatically for entities. You only need to write an entity provider if you are not choosing one of the following standard types.

6. Using @Consumes and @Produces to customize requests and responses
The @Produces annotation is used to specify the MIME media types or representations a resource can produce and send back to the client. If @Produces is applied at the class level, all the methods in a resource can produce the specified MIME types by default. If it is applied at the method level, it overrides any @Produces annotations applied at the class level.
If no methods in a resource are able to produce the MIME type in a client request, the Jersey runtime sends back an HTTP “406 Not Acceptable” error.
The value of @Produces is an array of String of MIME types.
If a resource class is capable of producing more than one MIME media type, the resource method chosen will correspond to the most acceptable media type as declared by the client. More specifically, the Accept header of the HTTP request declared what is most acceptable.

The @Consumes annotation is used to specify which MIME media types of representations a resource can accept, from the client. If @Consumes is applied at the class level, all the response methods accept the specified MIME types by default. If @Consumes is applied at the method level, it overrides any @Consumes annotations applied at the class level.
If a resource is unable to consume the MIME type of a client request, the Jersey runtime sends back an HTTP “415 Unsupported Media Type” error.
The value of @Consumes is an array of String of acceptable MIME types.

7. Extracting Request Parameters
Parameters of a resource method may be annotated with parameter-based annotations to extract information from a request. There are six types of parameters you can extract for use in your resource class: query parameters, URI path parameters, form parameters, cookie parameters, header parameters, and matrix parameters.
Query parameters are extracted from the request URI query parameters, and are specified by using the javax.rs.QueryParam annotation in the method parameter arguments.
@QueryParam and @PathParam can only be used on the following Java types: all primitive types except char, all wrapper class of primitive types except Character, have a constructor that accepts a single String argument, any class with the static method named valueOf(String) that accpets a single String argument, any class with a constructor that takes a single String as a parameter, List<T>, Set<T>, or SortedSet<T>, where T matches the already listed criteria. Sometimes parameters may contain more than one value for the same name. If this is the case, these types may be used to obtain all values.
If @DefaultValue is not used in conjunction with @QueryOaram, and the query parameter is not present in the request, then value will be an empty collection for List, Set, or SortedSet; null for other object types; and the Java-defined default for primitive types.
URI path parametes are extracted from the request URI, and the parameter names correspond to the URI path template variable names specified in the @Path class-level annotation. URI parameters are specified using the javax.ws.rs.PathParam annotation in the method paraeter arguments.
Form parameters extract information from a request representation that is of the MIME media type application/x-www-form-urlencoded and conforms to the encoding specified by HTML forms.
@Context can be used to obtain contextual Java types related to the request or response.

Java查漏补缺3-Swing中的文本绑定监听

在做简单的Swing开发中,会经常遇到这样的情况,需啊监听文本框的内容的变化。比如一个最简单的例子就是如果登陆框中用户名和密码都不为空的时候,登陆按钮才被激活。

在查看事件的时候,很多人第一眼看到的就是InputMethod下面的caretPositionChanged和inputMethodTextChanged。无论从文档还是从函数名字来看,这两个函数似乎都是我们所需要实现的方法。可惜当你填满了里面的内容,DEBUG的时候,会发现无论如何你改变你的输入框的内容,根本都不会触发这两个事件。在Sun的论坛上,有个人回了这样一句话来解决这个问题:“A component will only receive input method events from input methods if it also overrides getInputMethodRequests to return an InputMethodRequests instance.”

不愿意为这个问题多纠结,给出解决方案就是使用DocumentListener。这个接口提供了三个方法来侦听所绑定的文本的内容。对于我们需要的监听对象,直接添加这个侦听器,并实行其中的方法即可。

Document doc1 = jTextFieldId.getDocument();
doc1.addDocumentListener(new DocumentListener() {
            String name;
            String pwd;

            public void insertUpdate(DocumentEvent e) {
                name = jTextFieldId.getText();
                pwd = new String(jPasswordFieldPassword.getPassword());
                if(!name.equals("") && !pwd.equals("")){
                    jButtonSignIn.setEnabled(true);
                } else {
                    jButtonSignIn.setEnabled(false);
                }

            }

            public void removeUpdate(DocumentEvent e) {
                name = jTextFieldId.getText();
                pwd = new String(jPasswordFieldPassword.getPassword());
                if(!name.equals("") && !pwd.equals("")){
                    jButtonSignIn.setEnabled(true);
                } else {
                    jButtonSignIn.setEnabled(false);
                }
            }

            public void changedUpdate(DocumentEvent e) {

            }
        });

Java查漏补缺2 - 等于判断

基础的只是大家都很清楚,对于原始类型如int, float,直接用==就可以比较。对于Class类型的,在Java中使用的是reference的比较,因此需要使用的是equals.

Integer a1 = new Integer(1);
Integer a2 = new Integer(1);
System.out.println(a1==a2); //false
System.out.println(a1.equals(a2)); //true

因此在上述代码中,尽管使用的看上去就是int,但是因为实际已经使用了Integer,所以单纯使用==是不能比较的。

对于自己定义的类型,如果直接使用equals来比较,将会同样返回非期望的值。因为新定义的类,一般不会去重写equals的方法,而会直接调用其父类的equals方法,将也是直接比较reference。如果我们需要比较自定义的类型的时候,只需要重写equals方法就可以了。

class MyClass{
        private int count;
        public MyClass(int a){
            this.count=a;
        }

    @Override
    public boolean equals(Object obj) {
        MyClass newmc = (MyClass)obj;
        return this.count==newmc.count ? true : false;
    }

MyClass m1 = new MyClass(1);
MyClass m2 = new MyClass(1);
System.out.println(m1==m2);
System.out.println(m1.equals(m2));

Java查漏补缺1-Binding时间

在编程语言中,有两种binding方式,一是在compile期间binding,被称作Early-binding,一种是在execute期间binding,被称作Late-binding。
在Java中,更多的采用的是Late-binding这种方式。要做一个比较好的解释,可以看看关于继承的例子。以下这段代码显示了一个很简单的继承关系,父类是Shape,子类是Circle和Rectangle,其中分别实现了各自的draw()方法。

public class Shape {
    public void draw(){
        System.out.println("Draw shape");
    }
}

public class Circle extends Shape{
    public void draw(){
        System.out.println("Draw circle");
    }
}

public class Rectangle extends Shape{
    public void draw(){
        System.out.println("Draw rectangle");
    }
}

public class Test {
    public static void main(String[] args){
            Shape shape = new Shape();
            shape.draw();
            Shape shape1 = new Rectangle();
            shape1.draw();
            Shape shape2 = new Circle();
            shape2.draw();
        }
}

在以上的示例中,我们在main函数中看到生成了三个分别是以上三个类的实例,但是它们共用了同样的Shape声明。这也就是Java最重要的多态的之所在。不过不是本文的重点,略过。我们发现既然三个实例享有同样的声明,在编译期间,编译器是无法判断到底每一个实例属于的是哪一个类,需要调用的是哪一个draw方法。而在执行期间,run-time会自动根据new关键字对应的类型,将其binding上。这就是所谓的late-binding。在传统的大多数非OOP语言中,early-binding占据绝大多数。总之,early-binding意味着在程序编译期间,运行时系统将确定需要调用的函数的地址,而late-binding则会在系统运行时根据当时情况动态决定。(在C++中,也支持类似的late-binding,不过需要显示的加以virtual关键字)

一个复习中发现的Java问题

自认为接触了这么多年的Java,考点小考试应该问题不大,但是一复习起来做起题,发现问题多多。特别是一个关于DecimalFormat的问题,几乎耗费了两个小时。这些都是以前完全不关心的东西,把这些点小小的总结一下可算是查漏补缺。

DecimalFormat
大家都很熟悉在println中通过各种形式来规定输出格式,DecimalFormat也是一个相似的功能,服务于各种数据。其最基本的几个替换符为:
0 — 代替所有的0-9的数字,必须出现
# — 代替除0外的1-9数字,0的时候不出现
. — 小数点
E — 指数符号
% — 百分号
,– 用来分组的占位符,在西方表达中常见如1,234,568
还有一些,不列出来了,自己可以再文档中查到。我主要想说以下关于前四个最基本的组合。

给出一个数字67890.78987这个数字基本上可以吧我要提到的问题都囊括在里面。
首先前面的示例性的生成Pattern就几乎省略了,DecimalFormat df = new DecimalFormat(”/*这个里面插入你的模式*/”);

模式        结果                注释
0000.0000    67890.7899            0代表必须有的位数,小数点4位,小数点后4位。由于实际中小数点前比4位多,保留。小数点后比5位多,自动进位,四舍五入。
00000.000    67890.790            小数点后有3位,同时后面进位,所以变为790
000000.000    067890.790            小数点前有6位,实际数字只有5位,所以最前面一位必须用0补齐。
0000.000000    67890.789870             小数点后有6位,实际数字只有5位,用0补齐

####.####    67890.7899            同样小数点前最多4位,小数点后最多4位。同样因为实际数字小数点前比4位多,保留。后面四舍五入。
####.###    67890.79            小数点后最多3位,应该是790,但是由于是#,所以0可以去掉。
######.###    67890.79            小数点前最多6位,实际为5位,第一位补0,去掉。
####.######    67890.78987            小数点后最多6位,实际为5位,最后一位补0,去掉。

##0.00#    67890.79            在0和#组合中,仍然符合以上规则。小数点后最后一位补0,是#,去掉。

科学技术法的判断位数方法和前面的有所不同。首先,小数点前面的位数和小数点后的位数代表的都不再是实际看到的小数点前后的位数,而是后面的实际数字的情况。在最大整数位比最小整数位差距在1之上的情况下,E的指数大小为小数点前最多位数的整数倍。但是尾数的位数是最大整数尾数和最大小数位数之和。
000000.##E0    67890.79–678907.9E-1
00000.##E0    67890.79–67890.79E0        小数点前5位,尾数一共5+2=7位。指数为0。
#0000.##E0    67890.79–67890.79E0        同上
##000.##E0    67890.79–67890.79E0        小数点前最小3位,和本来5位差距在1之上,因此指数为5的倍数。
0000.##E0    67890.79–6789.08E1        小数点前4位,尾数一共4+2=6位。指数为1。
#000.##E0    67890.79–6.78908E4        小数点前最小3位,和本来5位差距在1之上,因此指数为4的倍数。
##00.##E0    67890.79–6.78908E4        小数点前最小2位,和本来5位差距在1之上。
000.##E0    67890.79–678.91E2
#00.##E0    67890.79–67.891E3        小数点前最小2位,和本来5位差距在1之上,指数为3的倍数。
##0.##E0    67890.79–67.891E3
00.##E0    67890.79–67.89E3
#0.##E0    67890.79–6.789E4
##.##E0    67890.79–6.789E4
0.##E0        67890.79–6.79E4
#.##E0        67890.79–6.79E4

Older Entries
  • English Version

    • Cannot read Chinese? Please take a look at my English site, hope you can find more you need there!
  • 感谢支持

  • twitter

    facebook

    linkedin

  • Categories