This paper analyzes the differences between jdk1.4, jdk1.5 and jdk1.6 in Java programming in combination with examples. Share it for your reference, as follows:
Simply put: There are two biggest differences between 1.4 and 1.5. One is that 1.5 has generics, and the other 1.5 can automatically encapsulate encapsulated data types of eight basic data types. That is, Integer a = 4 is not allowed. There is not much difference between 1.5 and 1.6. 1.6 I think the most changes are the GUI, which provides a lot of convenient layout management and extension.
During this period, I joined an e-government company and used weblogic8. Then let's use jdk1.4. Eclipse changed the jdk version. However, the previous projects have basically become popular.
★New features of jdk1.5:
1. Generics
2 Automatic packing/unboxing
3 for-each
4 static import
5 variable length parameters
1. Generics (avoid running errors that may be caused by type casting)
For example:
ArrayList list=new ArrayList(); list.add(new Integer(3)); list.add(new Integer(4)); int i=((Integer)(list.get(0))).parseInt();
Very troublesome
ArrayList<Integer>list=new ArrayList<Integer>(); list.add(new Integer(3)); list.add(new Integer(4)); int i=list.get(0).parseInt();
2 Automatic packing/unboxing
The last sentence of the above example can be changed to:
The code copy is as follows: int i=list.get(0);
Because the original type and the corresponding wrapper class do not need to be explicitly converted
3 for-each
Enhancement of loops
int a[]={......};//Initialize for(int i:a) { ...... }Don't use the previous i=0;i<a.length;i++
4 static import
Previously tuned Java.math
The code copy is as follows: Math.sqrt();
Now static import java.lang.Math.sqrt;
sqrt();
It's equivalent to having this method in your own class
5 variable length parameters
int sum(int ...intlist) { int sum; sum=0; for(int i=0;i<intlist.length;i++) { sum+=intlist[i]; } return sum;}There is any parameter, treat it as an array
★New features of jdk6.0
Enhanced for loop statement annotation enumeration of "hidden" static method variadic parameters (Vararg)
Wildcards and covariance return enhanced for loop statements
To iterate over sets and arrays, the enhanced for loop provides a simple, compatible syntax. There are two points worth mentioning:
1. In a loop, the initialization expression is calculated only once. int expression
Unenhanced For:
int sum = 0; Integer[] numbers = computeNumbers(); for (int i=0; i < numbers.length ; i++) sum += numbers[i];
Enhanced For:
int sum = 0; for ( int number: computeNumbers() ) sum += number;
limitation
Iterator or subscript cannot be accessed during enhanced for loop iteration
Please see the following example:
for (int i=0; i < numbers.length ; i++) {if (i != 0) System.out.print(",");System.out.print(numbers[i]);}Here is another example:
for (Iterator<integer> it = n.iterator() ; it.hasNext() ; )if (it.next() < 0)it.remove();
Comments
Comment processing is a big topic. Because this article focuses only on the core language features, we do not intend to cover all its possible forms and pitfalls. We will discuss built-in annotations (SuppressWarnings, Deprecated and Override) and the limitations of general annotation processing.
Suppress Warnings
This comment turns off compiler warnings at the class or method level. Sometimes you know more clearly than the compiler that the code must use a rejected method or perform some action that cannot statically determine whether type-safe is type-safe, and use:
@SuppressWarnings("deprecation")public static void selfDestruct() {Thread.currentThread().stop();}This is probably the most useful thing about built-in annotations. Unfortunately, javac for 1.5.0_04 does not support it. But 1.6 supports it, and Sun is working on porting it backwards into 1.5.
This annotation is supported in Eclipse 3.1, and other IDEs may also support it. This allows you to completely free the code from the warning. If there is a warning at compile time, you can be sure that you just added it - to help view code that may be unsafe. With the addition of generics, it will be more handy to use.
Deprecated
Unfortunately, Deprecated is not that useful. It was originally intended to replace the @deprecated javadoc tag, but since it does not contain any fields, there is no way to suggest what users of deprecated classes or methods should use as a replacement. most
Both the usage requires the javadoc tag and this annotation.
Override
Override says that the method it annotates should override methods with the same signature in the superclass:
@Overridepublic int hashCode() {...}Looking at the example above, if "C" is not capitalized in hashCode, there will be no errors at compile time, but the method will not be called as expected at runtime. By adding the Override tag, the compiler will prompt if it actually performed the rewrite.
This is also helpful in situations where superclasses change. If a new parameter is added to the method, and the method itself is renamed, the subclass will suddenly not be compiled because it no longer rewrites anything from the superclass.
Other notes
Comments are very useful in other scenarios. When it is not to modify the behavior directly but enhance the behavior, especially when adding boilerplate code, the annotation works very well in frameworks such as EJB and Web services.
Comments cannot be used as preprocessors. Sun's design specifically prevents the modification of the bytecode of the class entirely due to comments. This allows you to correctly understand the results of the language, and tools like IDE can also perform in-depth code analysis and refactoring.
The comment is not a silver bullet. When I first encountered it, people tried to try various techniques. Please see the following suggestions obtained from others:
public class Foo {@Propertyprivate int bar;}The idea is to automatically create getter and setter methods for private field bar. Unfortunately, there are two failures in this idea: 1) it doesn't work, and 2) it makes the code difficult to read and process. It is impossible to implement, because as mentioned earlier, Sun specifically prevents modification of classes with comments.
Even if possible, it is not a good idea because it makes the code poorly readable. The first time you see this code, you won't know that the comment creates the method. Also, if you need to perform some operations inside these methods in the future, comments are useless. In short, don't try to do things that regular code can do with comments.
enumerate
Enum is very much like a public static final int declaration, which has been used as an enum value for many years. The biggest and most obvious improvement to int is type safety - you cannot mistakenly replace another type with one type of enumeration, which is different from int, and all ints are the same for the compiler. With very few exceptions, all enum-style int structures should usually be replaced with enum instances.
Enumeration provides some additional features. The two practical classes EnumMap and EnumSet are standard set implementations specially optimized for enumeration. If you know that a collection contains only enum types, you should use these special collections instead of HashMap or HashSet.
In most cases, you can use enum to replace all public static final ints in the code. They are comparable and can be imported statically, so references to them appear to be equivalent, even for inner classes (or inner enum types). Note that when comparing enum types, the instructions that declare them indicate their sequential values.
"Hidden" static method
Two static methods appear in all enum type declarations. Because they are static methods on enum subclasses, not methods of Enum itself, they do not appear in javadoc of java.lang.Enum.
The first is values(), which returns an array of all possible values of the enumeration type.
The second is valueOf(), which returns an enum type for the provided string, which must exactly match the source code declaration.
method
One of our favorite aspects about enum types is that it can have methods. In the past you might have to write some code to convert the public static final int and convert it from the database type to a JDBC URL. Now, the enum type itself can be made with a complete
Methods for manipulating code. Here is an example, including the abstract method of DatabaseType enum type and the implementation provided in each enum instance:
public enum DatabaseType {ORACLE {public String getJdbcUrl() {...}},MYSQL {public String getJdbcUrl() {...}};public abstract String getJdbcUrl();}Now the enum type can directly provide its practical methods. For example:
DatabaseType dbType = ...; String jdbcURL = dbType.getJdbcUrl();
To get the URL, you must know in advance where the utility method is.
Vararg
Using mutable parameters correctly does clean up some junk code. A typical example is a log method with a variable number of String parameters:
Log.log(String code)Log.log(String code, String arg)Log.log(String code, String arg1, String arg2)Log.log(String code, String[] args)
When discussing variable parameters, it is interesting that if you replace the first four examples with new variable parameters, it will be compatible:
Copy the code as follows: Log.log(String code, String... args)
All mutable parameters are source compatible - that is, if all calling programs of the log() method are recompiled, all four methods can be replaced directly. However, if backward binary compatibility is required, then the first three methods need to be abandoned. Only the last method with a string array parameter is equivalent to the variadic version, so it can be replaced by the variadic version.
Type cast
If you want the caller to know which type of parameters should be used, you should avoid type casting with mutable parameters. Looking at the following example, the first hope is String, and the second hope is Exception:
Log.log(Object... objects) {String message = (String)objects[0];if (objects.length > 1) {Exception e = (Exception)objects[1];// Do something with the exception}} The method signature should be as follows, and the corresponding mutable parameters are declared using String and Exception respectively:
The code copy is as follows: Log.log(String message, Exception e, Object... objects) {...}
Do not use variable parameters to destroy the type system. It can only be used when it is strongly typed. For this rule, PrintStream.printf() is an interesting exception: it provides type information as its first argument so that those types can be accepted later.
Covariance return
The basic usage of covariant return is to avoid type casting when an implementation's return type is known to be more specific than the API. In the following example, there is a Zoo interface that returns an Animal object. Our implementation returns an AnimalImpl object, but before JDK 1.5, it must be declared to return an Animal object. :
public interface Zoo {public Animal getAnimal();}public class ZooImpl implements Zoo {public Animal getAnimal(){return new AnimalImpl();}}The use of covariant returns replaces three anti-patterns:
Direct field access. To circumvent API limitations, some implementations expose subclasses directly into fields:
Copy the code as follows: ZooImpl._animal
Another form is to perform a down conversion in the calling program, knowing that the implementation is actually a specific subclass:
The code copy is as follows:((AnimalImpl)ZooImpl.getAnimal()).implMethod();
The last form I've seen is a concrete method that is used to avoid problems caused by a completely different signature:
The code copy is as follows: ZooImpl._getAnimal();
These three modes have their problems and limitations. Either it is not neat enough or it exposes unnecessary implementation details.
Covariance
The covariant return mode is cleaner, safer and easy to maintain, and it does not require type casting or specific methods or fields:
public AnimalImpl getAnimal(){ return new AnimalImpl(); } Use results:
The code copy is as follows: ZooImpl.getAnimal().implMethod();
Using generics
We will learn about generics from two perspectives: using generics and constructing generics. We do not discuss the obvious usage of List, Set, and Map. It is sufficient to know that generic collections are powerful and should be used frequently.
We will discuss the use of generic methods and the compiler's method of inferring types. Usually none of these will go wrong, but when something goes wrong, the error message can be very confusing, so you need to know how to fix these issues.
Generic Methods
In addition to generic types, Java 5 also introduces generic methods. In this example from java.util.Collections, a single element list is constructed. The element type of the new List is inferred based on the type of the object passed in the method:
Copy the code as follows: static <T> List<T> Collections.singletonList(T o)
Example usage:
public List<Integer> getListOfOne() {return Collections.singletonList(1);}In the example usage, we pass in an int. So the return type of the method is List<Integer>. The compiler infers T to Integer. This is different from generic types, because you don't usually need to specify type parameters explicitly.
This also shows the interaction between autoboxing and generics. The type parameter must be a reference type: that's why we're getting List<Integer> instead of List<int>.
Generic methods without parameters
The emptyList() method is introduced with generics as a type-safe permutation of the EMPTY_LIST field in java.util.Collections:
Copy the code as follows: static <T> List<T> Collections.emptyList()
Example usage:
public List<Integer> getNoIntegers() {return Collections.emptyList();}Unlike the previous example, this method has no parameters, so how does the compiler infer the type of T? Basically, it will try to use the parameter once. If it doesn't work, it tries to use the return or assignment type again. In this example, the return is List<Integer>, so T is inferred as Integer.
What happens if a generic method is called outside the return statement or assignment statement? Then the compiler will not be able to perform the second transfer of type inference. In the following example, emptyList() is called from inside the conditional operator:
public List<Integer> getNoIntegers() {return x ? Collections.emptyList() : null;} Because the compiler cannot see the return context and cannot infer T, it abandons and takes Object. You will see an error message like: "Cannot convert List<Object> to List<Integer>."
To fix this error, type parameters should be passed explicitly to the method call. This way, the compiler will not try to infer type parameters and can get the correct result:
The code copy is as follows: return x ? Collections.<Integer>emptyList() : null;
Another place where this happens often is in method calls. If a method takes a List<String> parameter and needs to call the passed emptyList() for that parameter, then this syntax is also required.
Outside the collection
Here are three examples of generic types, which are not collections, but use generics in a novel way. All three examples come from standard Java libraries:
Copy the code as follows:Class<T>
Class is parameterized on the class type. This makes it possible to construct a newInstance without type casting.
Copy the code as follows: Comparable<T>
Comparable is parameterized by the actual comparison type. This provides stronger typing when comparedTo() calls. For example, String implements Comparable<String>. Calling compareTo() on anything other than String will fail at compile time.
Copy the code as follows: Enum<E extends Enum<E>>
Enum is parameterized by the enum type. An enum type named Color will extend Enum<Color>. The getDeclaringClass() method returns an enumeration type class object, in this example, a Color object. It's different from getClass(), which may return a nameless class.
Wildcard
The most complex part of generics is the understanding of wildcard characters. We will discuss three types of wildcards and their uses.
First let's understand how arrays work. You can assign a value from an Integer[] to a Number[]. If you try to write a Float to Number[], it can be compiled, but it will fail at runtime and an ArrayStoreException appears:
Integer[] ia = new Integer[5];Number[] na = ia;na[0] = 0.5; // compiles, but fails at runtime
If you try to convert the example directly into a generic, it will fail at compile time because the assignment is not allowed:
List<Integer> iList = new ArrayList<Integer>();List<Number> nList = iList; // not allowednList.add(0.5);
If you use generics, you will not encounter a runtime ClassCastException as long as the code does not appear when compiling.
Upper limit wildcard
What we want is a list of the exact element type unknown, which is different from arrays.
List<Number> is a list whose element type is the specific type Number.
List<? extends Number> is a list of the exact element type unknown. It is Number or its subtype.
Upper limit
If we update the initial example and assign the value to List<? extends Number>, then the assignment will be successful now:
List<Integer> iList = new ArrayList<Integer>();List<? extends Number> nList = iList;Number n = nList.get(0);nList.add(0.5); // Not allowed
We can get Number from the list because we can assign it to Number regardless of the exact element type of the list (Float, Integer, or Number).
We still cannot insert floating point types into the list. This will fail at compile time because we can't prove that this is safe. If we want to add floating point types to the list, it will break the initial type safety of iList - it only stores Integer.
Wildcards give us more expressive power than arrays.
Why use wildcards
In the following example, wildcards are used to hide type information from users of the API. Internally, the Set is stored as CustomerImpl. The API users only know that they are getting a Set from which they can read the Customer.
Wildcards are required here because it is impossible to assign values to Set<Customer> from Set<CustomerImpl>:
public class CustomerFactory {private Set<CustomerImpl> _customers;public Set<? extends Customer> getCustomers() {return _customers;}}Wildcard and covariant return
Another common usage of wildcard characters is to use it with covariant return. The same rules as assignment can be applied to covariant returns. If you want to return a more specific generic type in a rewritten method, the declared method must use a wildcard:
public interface NumberGenerator {public List<? extends Number> generate();}public class FibonacciGenerator extends NumberGenerator {public List<Integer> generate() {...}}If you want to use an array, the interface can return Number[], while the implementation can return Integer[].
Lower limit
What we are talking about is mainly about the upper limit wildcard. There is also a lower limit wildcard. List<? super Number> is a list of the exact "element type" unknown, but it may be Mnumber, or the supertype of Number. So it might be a List<Number> or a List<Object>.
Lower-limited wildcards are far less common than upper-limited wildcards, but they are required when they are needed.
Lower and upper limit
List<? extends Number> readList = new ArrayList<Integer>();Number n = readList.get(0);List<? super Number> writeList = new ArrayList<Object>();writeList.add(new Integer(5));
The first is a list of numbers that can be read from.
The second is a list of numbers to which you can write.
Unbounded wildcard
Finally, the content of a List<?> list can be of any type, and it is almost the same as List<? extends Object>. Objects can be read at any time, but content cannot be written to the list.
Wildcards in public APIs
In short, as mentioned earlier, wildcards are very important in hiding implementation details from the caller, but even if the lower bound wildcards seem to provide read-only access, they are not the case due to non-generic methods like remove(int position). If you want a truly unchanged collection, you can use the method on java.util.Collection like unmodifiableList().
Remember wildcards when writing APIs. Generally, when passing generic types, you should try using wildcards. It enables more callers to access the API.
By receiving List<? extends Number> instead of List<Number>, the following method can be called by many different types of lists:
The code copy is as follows: void removeNegatives(List<? extends Number> list);
Construct generic types
Now we will discuss constructing our own generic types. We will show some examples where type safety can be improved by using generics, and we will also discuss some common issues when implementing generic types.
Collection-like functions
The first example of a generic class is an example of collection style. Pair has two type parameters, and the field is an instance of type:
public final class Pair<A,B> {public final A first;public final B second;public Pair(A first, B second) {this.first = first;this.second = second;}}This makes it possible to return two items from a method without writing a dedicated class for each combination of two types. Another way is to return Object[], which is the type is unsafe or untidy.
In the following usage, we return a File and a Boolean from the method. The client of the method can use fields directly without type casting:
public Pair<File,Boolean> getFileAndWriteStatus(String path){// create file and statusreturn new Pair<File,Boolean>(file, status);}Pair<File,Boolean> result = getFileAndWriteStatus("...");File f = result.first; boolean writeable = result.second;Outside the collection
In the following example, generics are used for additional compile-time security. By parameterizing the DBFactory class to the created Peer type, you are actually forcing the Factory subclass to return a specific subtype of a Peer:
public abstract class DBFactory<T extends DBPeer> {protected abstract T createEmptyPeer();public List<T> get(String constraint) {List<T> peers = new ArrayList<T>();// database magicreturn peers;}}By implementing DBFactory<Customer>, CustomerFactory must return a Customer from createEmptyPeer():
public class CustomerFactory extends DBFactory<Customer>{public Customer createEmptyPeer() {return new Customer();}}Generic Methods
Whether you want to impose constraints on generic types between parameters and between parameters and return types, you can use generic methods:
For example, if the inversion function written is inverted positionally, then a generic method may not be required. However, if you want the inversion to return a new List, you may want the element type of the new List to be the same as the type of the incoming List. In this case, a generic method is needed:
Copy the code as follows: <T> List<T> reverse(List<T> list)
Concrete
When implementing a generic class, you may want to construct an array T[]. Because generics are implemented by erasure, this is not allowed.
You can try casting Object[] to T[]. But this is not safe.
Concrete solutions
According to the convention of generic tutorials, the solution uses a "type token". By adding a Class<T> parameter to the constructor, you can force the client to provide the correct class object for the class's type parameters:
public class ArrayExample<T> {private Class<T> clazz;public ArrayExample(Class<T> clazz) {this.clazz = clazz;}public T[] getArray(int size) {return (T[])Array.newInstance(clazz, size);}}In order to construct an ArrayExample<String>, the client must pass String.class to the constructor because the type of String.class is Class<String>.
Having a class object makes it possible to construct a group of a correct element type
I hope this article will be helpful to everyone's Java programming.