1) Generics are one of the more significant changes in Java SE5. Generics implement the concept of parameterized types, which allow multiple types. The term "generic" means "pertaining or appropriate to large groups of classes." The original intent of generics in programming languages was to allow the programmer the greatest amount of expressiveness possible when writing classes or methods, by loosening the constraints on the types that those classes or methods work with.
2) When you create an instance of a parameterized type, casts will be taken care of for you and the type correctness will be ensured at compile time. This seems like an improvement.
3) One of the primary motivations for generics is to specify what type of object a container holds, and to have that specification backed up by the compiler. So instead of Object, we’d like to use an unspecified type, which can be decided at a later time. To do this, you put a type parameter inside angle brackets after the class name, and then substitute an actual type when you use the class.
4) A tuple is simply a group of objects wrapped together into a single object. The recipient of the object is allowed to read the elements but not put new ones in. (This concept is also called a Data Transfer Object (or Messenger.) Tuples can typically be of any length, but each object in the tuple can be of a different type. Note that a tuple implicitly keeps its elements in order.
5) You cannot use primitives as type parameters. However, Java SE5 conveniently added autoboxing and autounboxing to convert from primitive types to wrapper types and back.
6) You can also parameterize methods within a class. The class itself may or may not be generic—this is independent of whether you have a generic method. As a guideline, you should use generic methods "whenever you can." That is, if it’s possible to make a method generic rather than the entire class, it’s probably going to be clearer to do so. In addition, if a method is static, it has no access to the generic type parameters of the class, so if it needs to use genericity it must be a generic method. To define a generic method, you simply place a generic parameter list before the return value.
7) Notice that with a generic class, you must specify the type parameters when you instantiate the class.(otherwise cause a compile warning) But with a generic method, you don’t usually have to specify the parameter types, because the compiler can figure that out for you. (either from the argument passed to method or from return type) This is called type argument inference. Type inference doesn’t work for anything other than assignment when it can only infer from its return type. If you pass the result of a method call as an argument to another method, the compiler will not try to perform type inference. Instead it will treat the method call as though the return value is assigned to a variable of type Object. (won't compile)
Commented By Sean: I think it's because that method can be overloaded or overrided, to determine which method to call , JVM need to know the runtime type of the arguement, so compiler cann't know the exact method to be called.
8) It is possible to explicitly specify the type in a generic method, although the syntax is rarely needed. To do so, you place the type in angle brackets after the dot and immediately preceding the method name. When calling a method from within the same class, you must use this before the dot, and when working with static methods, you must use the class name before the dot.
9) Java compiler won't complain if you assign a parameterized class object to an unparameterized reference(upcast), but will issue a warning if you do vice versa.
Commented By Sean: Compiler will issue a warning if you use a unparameterized reference for a parameterized type.
10) EnumSet is a Java SE5 tool for easy creation of Sets from enums. The static method EnumSet.range( ) is given the first and last elements of the range to create a Set including the enums in the range.
11) Although you can say ArrayList.class, you cannot say ArrayList<Integer>.class.(Acutally you cannot access static members of a class use the generic form of class. Because type parameter is only meaningful for instance.) Class.getTypeParameters( ) returns an array of TypeVariable objects that represent the type variables declared by the generic declaration. All you find out from it is the identifiers that are used as the parameter placeholders. (parameters instead of arguements) The cold truth is that there’s no information about generic parameter types available inside generic code. You can know things like the identifier of the type parameter and the bounds of the generic type—you just can’t know the actual type parameter(s) used to create a particular instance.
12) Java generics are implemented using erasure. This means that any specific type information is erased when you use a generic. Inside the generic, the only thing that you know is that you’re using an object. So List<String> and List< Integer> are, in fact, the same type at run time. Both forms are "erased" to their raw type, List.
13) In order to call a method from an object of generic class, we must assist the generic class by giving it a bound that tells the compiler to only accept types that conform to that bound. This reuses the extends keyword. (i.e. <T exteds List> then add() can be called from references of type T.) We say that a generic type parameter erases to its first bound. The compiler actually replaces the type parameter with its erasure.( so in the above case, T erases to List which makes no difference to use List directly instead of generics.)
14) Generics are only useful when you want to use type parameters that are more "generic" than a specific type(and all its subtypes) — that is, when you want code to work across multiple classes. As a result, the type parameters and their application in useful generic code will usually be more complex than simple class replacement. However, you can’t just say that anything of the form <T extends List> is therefore flawed. For example, if a class has a method that returns T, then generics are helpful, because they will then return the exact type.
15) If generics had been part of Java 1.0, the feature would not have been implemented using erasure—it would have used reification to retain the type parameters as first-class entities, so you would have been able to perform type-based language and reflective operations on type parameters. The core motivation for erasure is that it allows generified clients to be used with non-generified libraries, and vice versa. This is often called migration compatibility.
16) In an erasure-based implementation, generic types are treated as secondclass types that cannot be used in some important contexts. The generic types are present only during static type checking, after which every generic type in the program is erased by replacing it with a non-generic upper bound. For example, type annotations such as List<T> are erased to List, and ordinary type variables are erased to Object unless a bound is specified. (This is just for compile time type checking , at runtime there is no type information of generic type at all.)
17) The cost of erasure is significant. Generic types cannot be used in operations that explicitly refer to runtime types, such as instanceof operations and new expressions. Because all the type information about the parameters is lost, whenever you’re writing generic code you must constantly be reminding yourself that it only appears that you have type information about a parameter.
18) Using Array.newInstance( ) is the recommended approach for creating arrays in generics.
public class ArrayMaker<T> {
private Class<T> kind;
public ArrayMaker(Class<T> kind) { this.kind = kind; }
@SuppressWarnings("unchecked")
T[] create(int size) {
return (T[])Array.newInstance(kind, size);
}
19) Even though erasure removes the information about the actual type inside a method or class, the compiler can still ensure internal consistency in the way that the type is used within the method or class.
20) Because erasure removes type information in the body of a method, what matters at runtime is the boundaries: the points where objects enter and leave a method. These are the points at which the compiler performs type checks at compile time, and inserts casting code. All the action in generics happens at the boundaries—the extra compile-time check for incoming values, and the inserted cast for outgoing values. It helps to counter the confusion of erasure to remember that "the boundaries are where the action takes place."
21) You can compensate for erasure by introducing a type tag. This means you explicitly pass in the Class object for your type so that you can use it in type expressions.
22) You can’t create arrays of generics. The general solution is to use an ArrayList everywhere that you are tempted to create an array of generics. You can define a reference in a way that makes the compiler happy. But you can never create an array of that exact type (including the type parameters). The only way to successfully create an array of a generic type is to create a new array of the erased type, and cast that. The runtime type of the generic array can only be Object[].
23) Bounds allow you to place constraints on the parameter types that can be used with generics. Although this allows you to enforce rules about the types that your generics can be applied to, a potentially more important effect is that you can call methods that are in your bound types.
24) Because erasure removes type information, the only methods you can call for an unbounded generic parameter are those available for Object. If, however, you are able to constrain that parameter to be a subset of types, then you can call the methods in that subset. To perform this constraint, Java generics reuse the extends keyword. (i.e. <T extends List>)
25) Notice that wildcards are limited to a single bound.(i.e. List<? extends Date, Time> is not valid.)
26) You can assign an array of a derived type to an array reference of the base type. But you can only put that derrived type to that array reference.(otherwise, there will be runtime exception) While it dosen't apply to generic containers. A List of Apple is not type-equivalent to a List of Fruit, even if an Apple is a type of Fruit. The real issue is that we are talking about the type of the container, rather than the type that the container is holding. Unlike arrays, generics do not have built-in covariance. This is because arrays are completely defined in the language and can thus have both compile-time and runtime checks built in, but with generics, the compiler and runtime system cannot know what you want to do with your types and what the rules should be.
Commented By Sean : for example, if you assign a List<Apple> reference to List<Fruit> and use this reference to add a Fruit reference which points to a non-Apple object at runtime. During compile time, compiler won't complain when you add a Fruit reference to List<Fruit> reference. But at runtime, due to type erasure, JVM don't know whether the List should host an Apple , thus won't verify the object being added to it is an Apple. However compiler has added cast codes when you retrive object from List<Apple> reference, which may cost ClassCastException if the list contains non-Apple Fruit instance. But such kind of issue will still happen if you assign a parameterized type reference to a raw type.
27)
List<? extends Fruit> flist = new ArrayList<Apple>(); //or List<?> wlist = new ArrayList<Apple>();
Once you do this kind of "upcast"(subtype wildcard)you lose the ability to put anything in, even an Object.
28) While add( ) takes an argument of the generic parameter type, contains( ) and indexOf( ) take arguments of type Object. So when you specify an ArrayList <? extends Fruit >, the argument for add( ) becomes’? extends Fruit’. From that description, the compiler cannot know which specific subtype of Fruit is required there, so it won’t accept any type of Fruit. It doesn’t matter if you upcast the Apple to a Fruit first—the compiler simply refuses to call a method (such as add( )) if a wildcard is involved in the argument list.
Commented by Sean : Think this way, you can assign either ArrayList<Orange> or ArrayList<Apple> to ArrayList<? extends Fruit>, so it's unsafe to add any type to the ArrayList<? exntends Fruit>. But for ArrayList<? super Apple>, you know that the list contains a super type of Apple and it's safe to put an Apple to it whatever object it originally held.
29) It’s up to the generic class designer to decide which calls are "safe," and to use Object types for their arguments. To disallow a call when the type is used with wildcards, use the type parameter in the argument list.
30) It’s also possible to use supertype wildcards. Here, you say that the wildcard is bounded by any base class of a particular class, by specifying <? super MyClass> or even using a type parameter: <? super T> (although you cannot give a generic parameter a supertype bound; that is, you cannot say <T super MyClass>). This allows you to safely pass a typed object into a generic type.
31) Supertype bounds(wildcards) relax the constraints on what you can pass into a generic method.
32) The unbounded wildcard <?> appears to mean "anything," and so using an unbounded wildcard seems equivalent to using a raw type. List actually means "a raw List that holds any Object type," whereas List<?> means "a non-raw homogeneous List of some specific type, but we just don’t know what that type is." While unbounded wildcard has the same limitations as subtype wildcard (Any method that takes an argument of the type parameter is disallowed to invoke.) and supertype wildcard ( Return type of any method that returns the parameterized type will be Object.).
Commented By Sean : You cannot know what exact type you get when you get the object from a List<? super Apple> , it can be of any super class of Apple. (i.e. Class.getSuperClass() return a Class<? super T> whose newInstance() method can only return Object type) So it's all about the boundary of a method : the input and output.
33) One of the limitations you will discover in Java generics is that you cannot use primitives as type parameters (i.e. ArrayList<int>). Although autoboxing can help but it cann't help on auto-box primitive arrays to its wrapper arrays.
34) Making the class generic wouldn’t work for static methods or fields.
35) To avoid warning for
List<Widget> lw = (List<Widget>)in.readObject()
, you must use a new form of cast introduced in Java SE5, the cast via a generic class: Class.cast():
List<Widget> lw2 = List.class.cast(in.readObject());
Commented By Sean: Class.cast() method returns T , which here is List.
36) Generics doesn't support overloading, that is to say you cannot differentiate parameters via generic argument (i.e. List<String> and List<Integer> are same parameters)
37) To understand what curiously recurring (class A extends B<A>) means, try saying it aloud: "I’m creating a new class that inherits from a generic type that takes my class name as its parameter." What can the generic base type accomplish when given the derived class name? Well, generics in Java are about arguments and return types, so it can produce a base class that uses the derived type for its arguments and return types. It can also use the derived type for field types, even though those will be erased to Object.
38) This is the essence of CRG (Curiously Recurring Generics): The base class substitutes the derived class for its parameters. This means that the generic base class becomes a kind of template for common functionality for all its derived classes, but this functionality will use the derived type for all of its arguments and return values. That is, the exact type instead of the base type will be used in the resulting class.
39) What self-bounding (i.e. SelfBounded<T extends SelfBounded<T>> )does is require the use of the class in an inheritance relationship like this:
class A extends SelfBounded<A> {}
This forces you to pass the class that you are defining as a parameter to the base class.
It’s also possible to use self-bounding for generic methods. This prevents the method from being applied to anything but a self-bounded argument of the form shown.
Commented By Sean: If you define SelfBounded<T extends SelfBounded> , then you can define A extends SelfBounded<B> where B is the subclass of SelfBounded.
40) The value of self-bounding types is that they produce covariant argument types—method argument types vary to follow the subclasses. Although self-bounding types also produce return types that are the same as the subclass type, this is not so important because covariant return types were introduced in Java SE5.(Return type of overridden method is allowed to be the subtype of the original return type) Without self-bounding, the ordinary inheritance mechanism steps in, and you get overloading, just as with the non-generic case:
class GenericSetter<T> { // Not self-bounded
void set(T arg){
System.out.println("GenericSetter.set(Base)");
}
}
class DerivedGS extends GenericSetter<Base> {
void set(Derived derived){
System.out.println("DerivedGS.set(Derived)");
}
}
public class PlainGenericInheritance {
public static void main(String[] args) {
Base base = new Base();
Derived derived = new Derived();
DerivedGS dgs = new DerivedGS();
dgs.set(derived);
dgs.set(base); // Compiles: overloaded, not overridden!
}
} /* Output:
DerivedGS.set(Derived)
GenericSetter.set(Base)
*/
41) Because you can pass generic containers to pre-Java SE5 code, there’s still the possibility that old-style code can corrupt your containers. Java SE5 has a set of utilities in java.util.Collections to solve the type-checking problem in this situation: the static methods checkedCollection( ), checkedList( ), checkedMap( ), checkedSet( ), checkedSortedMap( ) and checkedSortedSet( ). Each of these takes the container you want to dynamically check as the first argument and the type that you want to enforce as the second argument.
42) Because of erasure, the use of generics with exceptions is extremely limited. A catch clause cannot catch an exception of a generic type, because the exact type of the exception must be known at both compile time and run time. Also, a generic class can’t directly or indirectly inherit from Throwable (this further prevents you from trying to define generic exceptions that can’t be caught).
43) However, type parameters may be used in the throws clause of a method declaration. This allows you to write generic code that varies with the type of a checked exception.
interface Processor<T, E extends Exception> {
void process(List<T> resultCollector) throws E;
}
class ProcessRunner<T,E extends Exception> extends ArrayList<Processor<T,E>> {
List<T> processAll() throws E {
List<T> resultCollector = new ArrayList<T>();
for(Processor<T,E> processor : this)
processor.process(resultCollector);
return resultCollector;
}
}
44) The term mixin seems to have acquired numerous meanings over time, but the fundamental concept is that of mixing in capabilities from multiple classes in order to produce a resulting class that represents all the types of the mixins. This is often something you do at the last minute, which makes it convenient to easily assemble classes.
45) Erasure forgets the base-class type, so a generic class cannot inherit directly from a generic parameter:
public Mixins { //not allowed class Function1<T> extends T { void function1() {} } //not allowed class Function2<T> extends T { void function2() {} } class BaseFunction { void baseFunction() {} } public static void main(String[] args) { //define mixins as instance of BaseFunction, Function1 and Function2 Function2<Function1<BaseFunction> mixins = new Function2<>(); mixins.function1(); mixins.function2(); mixins.baseFunction(); } }
46) Decorator pattern uses layered objects to dynamically and transparently add responsibilities to individual objects. Decorator specifies that all objects that wrap around your initial object have the same basic interface. Something is decoratable, and you layer on functionality by wrapping other classes around the decoratable. This makes the use of the decorators transparent. There are a set of common messages you can send to an object whether it has been decorated or not. A significant drawback to Decorator is that it only effectively works with one layer of decoration (the final one).
47) It’s possible to use a dynamic proxy to create a mechanism that more closely models mixins. Because of the constraints of dynamic proxies, each class that is mixed in must be the implementation of an interface:
class MixinProxy implements InvocationHandler {
Map<String,Object> delegatesByMethod;
public MixinProxy(TwoTuple<Object,Class<?>>... pairs) {
delegatesByMethod = new HashMap<String,Object>();
for(TwoTuple<Object,Class<?>> pair : pairs) {
for(Method method : pair.second.getMethods()) {
String methodName = method.getName();
// The first interface in the map
// implements the method.
// Will have issues if two method have same name in different interface
if (!delegatesByMethod.containsKey(methodName))
delegatesByMethod.put(methodName, pair.first);
}
}
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Object delegate = delegatesByMethod.get(methodName);
return method.invoke(delegate, args);
}
@SuppressWarnings("unchecked")
public static Object newInstance(TwoTuple... pairs) {
Class[] interfaces = new Class[pairs.length];
for(int i = 0; i < pairs.length; i++) {
interfaces[i] = (Class)pairs[i].second;
}
ClassLoader cl = pairs[0].first.getClass().getClassLoader();
return Proxy.newProxyInstance( cl, interfaces, new MixinProxy(pairs));
}
}
public class DynamicProxyMixin {
public static void main(String[] args) {
Object mixin = MixinProxy.newInstance(
tuple(new BasicImp(), Basic.class),
tuple(new TimeStampedImp(), TimeStamped.class),
tuple(new SerialNumberedImp(),SerialNumbered.class));
Basic b = (Basic)mixin;
TimeStamped t = (TimeStamped)mixin;
SerialNumbered s = (SerialNumbered)mixin;
b.set("Hello");
System.out.println(b.get());
System.out.println(t.getStamp());
System.out.println(s.getSerialNumber());
}
} /* Output: (Sample)
Hello
1132519137015
1
*/
48) Erasure requires that you specify the bounds of the generic types that may be used, in order to safely call specific methods for the generic objects in your code. However generic code typically only calls a few methods on a generic type, and a language with latent typing(structural typing or duck typing) loosens the constraint (and produces more generic code) by only requiring that a subset of methods be implemented, not a particular class or interface. Because of this, latent typing allows you to cut across class hierarchies, calling methods that are not part of a common interface. Because generics were added to Java late in the game, there was no chance that any kind of latent typing could be implemented, so Java has no support for this feature. We are forced to use a class or an interface and specify it in a bounds expression. In this case, generics were simply not necessary, since the classes were already forced to implement the common interface.
Commented By Sean: however with generics, you can accept and produce a specific type instead of a common interface.
49) Through reflection, we are able to dynamically establish whether the desired methods are available in the object and call them. 49) Because the Java designers (understandably) did not see the need for an "Addable" interface, we are constrained within the Collection hierarchy, and any other class even though it has an add( ) method, will not work. Because it is thus constrained to working with Collection, the code is not particularly "generic." With latent typing, this would not be the case. Because you are not at the mercy of the past design decisions of any particular library creator, so you do not have to rewrite your code every time you encounter a new library that didn’t take your situation into account (thus the code is truly "generic").
50) Latent typing means that you could write code saying, "I don’t care what type I’m using here as long as it has these methods." In effect, latent typing creates an implicit interface containing the desired methods. So it follows that if we write the necessary interface by hand (since Java doesn’t do it for us), that should solve the problem. Writing code to produce an interface that we want from an interface that we have is an example of the Adapter design pattern. We can use adapters to adapt existing classes to produce the desired interface, with a relatively small amount of code. ( use composition to adapt a base class of a hierarchy and use inheritance to adapt a specific type.)
51) Strategy design pattern completely isolates "the thing that changes" inside of a function object. A function object is an object that in some way behaves like a function—typically, there’s one method of interest. The value of function objects is that, unlike an ordinary method, they can be passed around, and they can also have state that persists across calls. The intent of the function object is primarily to create something that behaves like a single method that you can pass around; thus it is closely coupled with—and sometimes indistinguishable from—the Strategy design pattern. We are creating function objects which perform adaptation, and they are being passed into methods to be used as strategies:
// Different types of function objects: interface Combiner<T> { T combine(T x, T y); } interface UnaryFunction<R,T> { R function(T x); } interface Collector<T> extends UnaryFunction<T,T> { T result(); // Extract result of collecting parameter } interface UnaryPredicate<T> { boolean test(T x); } public class Functional { // Calls the Combiner object on each element to combine // it with a running result, which is finally returned: public static <T> T reduce(Iterable<T> seq, Combiner<T> combiner) { Iterator<T> it = seq.iterator(); if(it.hasNext()) { T result = it.next(); while(it.hasNext()) result = combiner.combine(result, it.next()); return result; } // If seq is the empty list: return null; // Or throw exception } // Take a function object and call it on each object in // the list, ignoring the return value. The function // object may act as a collecting parameter, so it is // returned at the end. public static <T> Collector<T> forEach(Iterable<T> seq, Collector<T> func) { for(T t : seq) func.function(t); return func; } // Creates a list of results by calling a // function object for each object in the list: public static <R,T> List<R> transform(Iterable<T> seq, UnaryFunction<R,T> func) { List<R> result = new ArrayList<R>(); for(T t : seq) result.add(func.function(t)); return result; } // Applies a unary predicate to each item in a sequence, // and returns a list of items that produced "true": public static <T> List<T> filter(Iterable<T> seq, UnaryPredicate<T> pred) { List<T> result = new ArrayList<T>(); for(T t : seq) if(pred.test(t)) result.add(t); return result; } // To use the above generic methods, we need to create // function objects to adapt to our particular needs: static class IntegerAdder implements Combiner<Integer> { public Integer combine(Integer x, Integer y) { return x + y; } } static class IntegerSubtracter implements Combiner<Integer> { public Integer combine(Integer x, Integer y) { return x - y; } } static class BigDecimalAdder implements Combiner<BigDecimal> { public BigDecimal combine(BigDecimal x, BigDecimal y) { return x.add(y); } } static class BigIntegerAdder implements Combiner<BigInteger> { public BigInteger combine(BigInteger x, BigInteger y) { return x.add(y); } } static class AtomicLongAdder implements Combiner<AtomicLong> { public AtomicLong combine(AtomicLong x, AtomicLong y) { // Not clear whether this is meaningful: return new AtomicLong(x.addAndGet(y.get())); } } // We can even make a UnaryFunction with an "ulp" // (Units in the last place): static class BigDecimalUlp implements UnaryFunction<BigDecimal,BigDecimal> { public BigDecimal function(BigDecimal x) { return x.ulp(); } } static class GreaterThan<T extends Comparable<T>> implements UnaryPredicate<T> { private T bound; public GreaterThan(T bound) { this.bound = bound; } public boolean test(T x) { return x.compareTo(bound) > 0; } } static class MultiplyingIntegerCollector implements Collector<Integer> { private Integer val = 1; public Integer function(Integer x) { val *= x; return val; } public Integer result() { return val; } } public static void main(String[] args) { // Generics, varargs & boxing working together: List<Integer> li = Arrays.asList(1, 2, 3, 4, 5, 6, 7); Integer result = reduce(li, new IntegerAdder()); print(result); result = reduce(li, new IntegerSubtracter()); print(result); print(filter(li, new GreaterThan<Integer>(4))); print(forEach(li, new MultiplyingIntegerCollector()).result()); print(forEach(filter(li, new GreaterThan<Integer>(4)), new MultiplyingIntegerCollector()).result()); MathContext mc = new MathContext(7); List<BigDecimal> lbd = Arrays.asList( new BigDecimal(1.1, mc), new BigDecimal(2.2, mc), new BigDecimal(3.3, mc), new BigDecimal(4.4, mc)); BigDecimal rbd = reduce(lbd, new BigDecimalAdder()); print(rbd); print(filter(lbd, new GreaterThan<BigDecimal>(new BigDecimal(3)))); // Use the prime-generation facility of BigInteger: List<BigInteger> lbi = new ArrayList<BigInteger>(); BigInteger bi = BigInteger.valueOf(11); for(int i = 0; i < 11; i++) { lbi.add(bi); bi = bi.nextProbablePrime(); } print(lbi); BigInteger rbi = reduce(lbi, new BigIntegerAdder()); print(rbi); // The sum of this list of primes is also prime: print(rbi.isProbablePrime(5)); List<AtomicLong> lal = Arrays.asList( new AtomicLong(11), new AtomicLong(47), new AtomicLong(74), new AtomicLong(133)); AtomicLong ral = reduce(lal, new AtomicLongAdder()); print(ral); print(transform(lbd,new BigDecimalUlp())); } } /* Output: 28 -26 [5, 6, 7] 5040 210 11.000000 [3.300000, 4.400000] [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] 311 true 265 [0.000001, 0.000001, 0.000001, 0.000001] */
相关推荐
This is a handy guide of sorts for any computer science engineering Students, Thinking In C# Programming is a solution bank for various complex problems related to C# and .NET. It can be used as a ...
■Chapter 15: Dealing with Threads Getting Through the Handlers Messages Runnables Running in Place Where Oh Where Has My UI Thread Gone? Asyncing Feeling The Theory AsyncTask, Generics, and Var...
Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:html、javascript、Vue 后台框架:SpringBoot 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4 后台路径地址:localhost:8080/项目名称/admin/dist/index.html 前台路径地址:localhost:8080/项目名称/front/index.html (无前台不需要输入)
本次分享的数据为1896年-2024年(从雅典到巴黎)间奥运会奖牌数据,包括年份、届次、国家地区、名次、金牌、银牌、铜牌等数据,含免费下载链接 ## 一、数据介绍 数据名称:历届奥运会奖牌数据 数据范围:世界各国 样本数量:1877条 数据年份:1896年-2024年 数据说明:包括届次、国家、名次等数据
Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:html、javascript、Vue 后台框架:SpringBoot 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4 后台路径地址:localhost:8080/项目名称/admin/dist/index.html 前台路径地址:localhost:8080/项目名称/front/index.html (无前台不需要输入)
【人机交互】MATLAB直车道线检测
Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:jsp 后台框架:SSM 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4
Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:jsp 后台框架:SSM 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4
2023年计算机硬件的组装实验报告.pdf
ava项目springboot基于springboot的课程设计,包含源码+数据库+毕业论文
内容概要:本文档主要针对含有质量平衡段(即弹性轴和重心重合点xa=0)的硬翼Flutter问题提供了MATLAB解决方案。文档通过迭代的方式对一系列参数(如频率比(fr)、弹性轴(E)和半径(r)等)进行操作,并利用贝塞尔函数(Kn)来评估flutter速度(UFhat),从而预测了不同质比(mu)下flutter的缩减速度变化情况。同时,文档包含了绘图命令以视觉展示减小颤振速度随质量比变化的趋势以及相应的MATLAB代码。 适合人群:航空工程、飞行器动力学领域的科研工作者,工程师及研究生。尤其是那些从事飞行安全性和稳定性研究的专业人士。 使用场景及目标:主要用于解决飞行器设计过程中遇到的具体颤振问题,能够为设计新型飞机或其他有翼飞行物体提供科学依据和技术支持。它还能够辅助教育,帮助相关专业的学生理解flutter现象及其预防措施。 其他说明:此文件是以数值方法探讨带质量平衡的翅膀颤振特性的实例,在工程上有着重要意义。对于希望深入学习此类问题的人来说,这是一个极好的参考资料和实验平台。然而,实际应用还需要进一步考虑真实条件下的复杂因素,因此需要更多的专业知识和背景资料的支持。
本次项目是设计一个基于JAVA的机场航班起降与协调管理系统。 (1)在经济可行性上来分析的话,该软件是机场内部使用的一个指挥协调软件,属于航空安全投资,本软件开发成本并不高,软件和服务器数据库可以用机场原有的数据库进行开发,比起空难给航空公司造成的损失来说九牛一毛。 (2)在技术可行性上来分析的话,该软件主要运用了Java技术、jQuery-easyui和Mysql数据库技术。Java是到目前来说最稳定的、最可靠的软件开发工具;jQuery-easyui虽然是比较新的前台开发技术,但是他的界面新颖整洁,适合于功能性软件的开发;Mysql数据库也是许多大公司都采用的软件项目开发数据库,不仅稳定而且性能可靠,可以用作本次软件的开发。 (3)在法律可行性上来分析的话,该软件使用的技术都为开源的软件开发工具和语言,虽然Java等开发技术都存在Sun公司的版权问题,但是Java技术是可以免费使用的,没有涉及到法律上的侵权。 (4)在方案可行性上来分析的话,此次软件开发的很大一部分精力都放在了软件的需求分析和设计方面,设计出来的软件可以很好地去实现我们所要完成的软件预先设计的功能。
2023年计算机与通信网络实验报告.pdf
2023年四川省德阳市统招专升本计算机自考真题(含答案).pdf
Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:jsp 后台框架:SSM 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4
Java项目基于springboot的课程设计,包含源码+数据库+毕业论文
Java 项目,仅供学习参考。 Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:html、javascript、Vue 后台框架:SpringBoot 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4 后台路径地址:localhost:8080/项目名称/admin/dist/index.html 前台路径地址:localhost:8080/项目名称/front/index.html (无前台不需要输入)
GUI面板MATLAB答题纸试卷自动识别
Java 项目, Java 毕业设计,Java 课程设计,基于 SpringBoot 开发的,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:html、javascript、Vue 后台框架:SpringBoot 开发环境:idea 数据库:MySql(建议用 5.7 版本,8.0 有时候会有坑) 数据库工具:navicat 部署环境:Tomcat(建议用 7.x 或者 8.x 版本), maven 2. 部署 如果部署有疑问的话,可以找我咨询 Java工具包下载地址: https://pan.quark.cn/s/eb24351ebac4 后台路径地址:localhost:8080/项目名称/admin/dist/index.html 前台路径地址:localhost:8080/项目名称/front/index.html (无前台不需要输入)
ava项目springboot基于springboot的课程设计,包含源码+数据库+毕业论文