Annotation
- The role of Annotation:
- Not the program itself, it can be interpreted
- Can be read by other programs (e.g., compilers, etc.)
- Format of Annotation:
- Such as @SuppressWarnings(value="unchecked")
- Where to use:
- package, class, method, field
- Accessible through reflection mechanism
Built-in Notes
import java.util.ArrayList; import java.util.List; public class Test01 extends Object{ //Annotation for @Override override @Override public String toString() { return super.toString(); } //@Deprecated is not recommended for programmers, but can be used, or there is a better way @Deprecated public static void test(){ System.out.println("Deprecated"); } //@SuppressWarnings Suppression Warning @SuppressWarnings("all") public void test02(){ List list = new ArrayList(); } public static void main(String[] args) { test(); } }
meta annotation
- The purpose of meta-annotations is to annotate other annotations. Java defines four standard meta-annotation types that are used to provide descriptions of other annotation types
- These types and the classes they support are in java. You can find it in the lang.annotation package (@Target, @Retention, @Documented, @Inherited)
- @Target: Used to describe the scope of the note
- @Retention: Indicates at what level the comment information needs to be saved
- (SOURCE<CLASS<RUNTIME)
- @Document: Indicates that the comment will be included in javadoc
- @Inherited: Indicates that a subclass can inherit this comment from its parent
Custom Notes
- When using @interface to customize annotations, Java is automatically inherited. Lang.annotation. Annotation interface
- Analysis:
- @interface is used to declare a comment in the format: public @ interface comment name {Define Content}
- Each of these methods actually declares a configuration parameter
- The name of the method is the name of the parameter
- The return value type is the type of the parameter {The return value can only be the base type, Class,String,enum}
- Default values of parameters can be declared by default
- If there is only one parameter member, the general parameter name is value
- Annotation elements must have values. When we define annotation elements, we often use the empty string 0 as the default value
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //Custom Notes public class Test03 { //Annotations can be explicitly assigned, and if there is no default, we must copy them @MyAnnotation2(age = 18) public void test(){} @MyAnnotation3("Qin Xinjiang") public void test2(){ } } @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation2{ //Noted parameters: parameter type + parameter name (); String name() default ""; int age(); int id() default -1;//If the default value is -1, it does not exist String[] schools() default {"Western Open Source","Tsinghua University"}; } @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation3{ String value();//If there is only one parameter, setting the parameter name to value omits it by default }
reflex
- Dynamic languages, languages that can change their structure at runtime, such as Object-C, C#, JavaScript, PHP, Python
- Static languages, languages with variable runtime structure, such as Java, C, C++.
- Java is dynamic, and we can use reflection to get features like dynamic languages
Java Reflection
-
Reflection mechanism allows a program to obtain internal information of any class during execution by means of the Reflection API, and to directly manipulate the internal properties and methods of any object
-
Class c = Class.forName("java.lang.String")
-
Once the class is loaded, a Class-type object (a class has only one Class object) is generated in the method area of heap memory, which contains the complete class structure information. From this object we can see the structure of the class. This object is like a mirror through which we see the institutions of classes, so we call it: reflection
-
Functions provided by the Java reflection mechanism
- Determine which class any object belongs to at run time
- Construct objects of any class at run time
- Determine at runtime which member variables and methods any class has
- Getting generic information at run time
- Call member variables and methods of any object at run time
- Processing annotations at run time
- Generate dynamic proxy
-
Reflection-related key API s
- java.lang.Class: represents a class
- java.lang.reflect.Method: The method that represents the class
- java.lang.reflect.Field: Member variable representing class
- java.lang.reflect.Constructor: Constructor representing a class
Class class
The following method is defined in the Object class and will be inherited by all subclasses
public final Class getClass()
- The type of return value of the above method is a Class class, which is the source of Java reflection. In fact, the so-called reflection is also well understood from the results of the program, that is, the name of the class can be derived from object reflection.
//What is Reflection public class Test02 { public static void main(String[] args) throws ClassNotFoundException { //Getting Class Objects of Classes by Reflection Class c1 = Class.forName("com.annotation_reflection.reflection.User"); System.out.println(c1); Class c2 = Class.forName("com.annotation_reflection.reflection.User"); Class c3 = Class.forName("com.annotation_reflection.reflection.User"); Class c4 = Class.forName("com.annotation_reflection.reflection.User"); //A class has only one Class object in memory //When a class is loaded, the entire structure of the class is encapsulated in the Class object System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); System.out.println(c4.hashCode()); } } //Entity class: pojo, entiry class User{ private String name; private int id; private int age; public User() { } public User(String name, int id, int age) { this.name = name; this.id = id; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", id=" + id + ", age=" + age + '}'; } }
- The information you can get by comparing mirrors is the properties, methods, and constructors of a Class, and what interfaces a Class actually implements. For each Class, the JRE maintains an object of the same Class type for it.
- Class itself is a class
- Class objects can only be created by the system
- A loaded class will only have one Class instance in the JVM
- A class object corresponds to one loaded into the JVM. class file
- Instances of each class will remember which Class instance they were generated from
- Class gives you a complete picture of all the loaded structures in a class
- Class classes are the root of Reflection s. For any class that you want to dynamically load and run, you must first get the corresponding Class object
Method Name | Functional description |
---|---|
static ClassforName(String name) | Returns a lass object with the specified Class name |
Object newInstance() | Call the default constructor to return an instance of the Class object |
getName() | Returns the name of the entity (class, interface, array class or void) represented by this Class object |
Class getSuperClass() | Returns the Class object of the parent class of the current Class object |
Class[] getinterfaces() | Gets the interface of the current Class object |
ClassLoader getClassLoader() | Returns the class loader for this class |
Constructor[] getConstructors() | Returns an array containing some Constructor objects |
Method getMothed(String name,Class.. T) | Returns a Method object with a parameter type of paramType |
Field[] getDeclaredFields() | Returns an array of Field objects |
- How Class Classes are created
//Test how Class classes are created public class Test03 { public static void main(String[] args) throws ClassNotFoundException { Person person = new Student(); System.out.println("This person is:"+person.name); //Mode 1: Obtain by Object Class c1 = person.getClass(); System.out.println(c1.hashCode()); //Mode 2: forname acquisition Class c2 = Class.forName("com.annotation_reflection.reflection.Student"); System.out.println(c2.hashCode()); //Mode 3: Pass the class name. Class acquisition Class c3 = Student.class; System.out.println(c3.hashCode()); //Mode 4: Packaging classes of basic built-in types all have a Type attribute Class c4 = Integer.TYPE; System.out.println(c4); //Get parent type Class c5 = c1.getSuperclass(); System.out.println(c5); } } class Person{ public String name; public Person(){ } public Person(String name){ this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } } class Student extends Person{ public Student(){ this.name = "Student"; } } class Teacher extends Person{ public Teacher(){ this.name = "Teacher"; } }
- Which types can have Class objects
- Class: external class, member (member internal class, static internal class), local internal class, anonymous internal class
- Interface: interface
- []: Array
- enum: enumeration
- annotation: comment@interface
- primitive type: basic data type
- void
import java.lang.annotation.ElementType; //All Types of Class public class Test04 { public static void main(String[] args) { Class c1 = Object.class; //class Class c2 = Comparable.class; //Interface Class c3 = String[].class; //One-dimensional array Class c4 = int[][].class; //2-D Array Class c5 = Override.class; //annotation Class c6 = ElementType.class; //enumeration Class c7 = Integer.class; //Basic data types Class c8 = void.class; //void Class c9 = Class.class; //Class System.out.println(c1); System.out.println(c2); System.out.println(c3); System.out.println(c4); System.out.println(c5); System.out.println(c6); System.out.println(c7); System.out.println(c8); System.out.println(c9); //As long as the element type is the same as the dimension, it is the same Class int[] a = new int[10]; int[] b = new int[100]; System.out.println(a.getClass().hashCode()); System.out.println(b.getClass().hashCode()); } }
Class Load Memory Analysis
Java memory analysis:
Class loading process:
Class loading and lassLoader understanding
- Load: Load the class file byte code content into memory, convert the static data into a runtime data structure for the method area, and generate a Java representing this class. Lang.Class object
- Link: The process of merging the binary code of a Java class into the running state of the JVM
- Verification: Ensure that the loaded class information conforms to the JVM specification and that there are no security issues
- Prepare: The stage in which memory is formally allocated for class variables (static) and default initialization of class variables is set, which will be allocated in the method area
- Resolution: The process of replacing a symbolic reference (constant name) with a direct reference (address) in a virtual machine constant pool
- Initialization:
- The process of executing the class constructor () method. Class constructor () methods are generated by the compiler automatically collecting assignment actions for all class variables in a class and merging statements in static code blocks. (Class constructors construct class information, not objects of that type).
- When you initialize a class, if you find that its parent has not been initialized, you need to trigger the initialization of its parent first.
- A virtual opportunity ensures that a class () method is properly locked and synchronized in a multithreaded environment.
public class Test05 { public static void main(String[] args) { A a = new A(); System.out.println(A.m); /* 1.Loading into memory results in a class corresponding to the Class object 2.Link, m = 0 at end of link 3.Initialization <clinit>(){ System.out.println("A Class static code block initialization "; m = 300; m = 100; } m = 100 */ } } class A{ static { System.out.println("A Class static code block initialization"); System.out.println(A.m); m = 300; } static int m = 100; public A() { System.out.println("A Initialization of class parameterless construction"); } }
When class initialization occurs
- Active reference to class (class initialization must occur)
- When the virtual machine starts, initialize the class where the main method is located first
- Object of a new class
- Invoke static members of classes (except final constants) and static methods
- Use java. The method of the lang.reflect package makes a reflection call to the class
- When a class is initialized, its parent is initialized first if its parent is not initialized
- Passive reference to class (class initialization will not occur)
- When accessing a static domain, only classes that actually declare the domain will be initialized. For example, when a static variable of the parent class is referenced through a subclass, it does not cause the subclass to initialize
- Defining class references through arrays does not trigger initialization of this class
- Reference constants do not trigger initialization of this class (constants are stored in the constant pool of the calling class during the link phase)
//When will the test class be initialized public class Test06 { static { System.out.println("Main Class is loaded"); } public static void main(String[] args) throws ClassNotFoundException { //1. Active Reference //Son son = new Son(); //Reflection also produces active references //Class.forName("com.annotation_reflection.reflection.Son"); //Methods that do not produce references to classes //System.out.println(Son.b); //Son[] array = new Son[5]; System.out.println(Son.M); } } class Father{ static int b = 2; static { System.out.println("Parent Class Loaded"); } } class Son extends Father{ static { System.out.println("Subclasses are loaded"); m = 300; } static int m = 100; static final int M = 1; }
Role of Class Loader
- What class loading does: Loads the class file byte code content into memory, converts the static data into a runtime data structure for the method area, and generates a Java in the heap representing this class. The lang.Class object, which acts as an access entry to the class data in the method area.
- Class caching: The standard JavaSE class loader can find classes as required, but once a class is loaded into the class loader, it will maintain loading (caching) for some time. However, the JVM garbage collection mechanism can recycle these Class objects
- The purpose of the class loader is to load classes into memory. The JVM specification defines loaders for the following types of classes
public class Test07 { public static void main(String[] args) throws ClassNotFoundException { //Get Loader for System Classes ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); //Get Parent Class Loader for System Class Loader-->Extension Class Loader ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent); //Get the parent loader of the extension class loader-->Root loader (C/c++) ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //Which loader loads when testing the current class ClassLoader classLoader = Class.forName("com.annotation_reflection.reflection.Test07").getClassLoader(); System.out.println(classLoader); //Test who loaded the JDK built-in classes classLoader = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader); //How to get the path that the system class loader can load System.out.println(System.getProperty("java.class.path")); //Parent Delegation Mechanism Multiple Detection for Security, Duplicate Invalid //java.lang.String--> /* D:\environment\java\java_update\jre\lib\charsets.jar; D:\environment\java\java_update\jre\lib\deploy.jar; D:\environment\java\java_update\jre\lib\ext\access-bridge-64.jar; D:\environment\java\java_update\jre\lib\ext\cldrdata.jar; D:\environment\java\java_update\jre\lib\ext\dnsns.jar; D:\environment\java\java_update\jre\lib\ext\jaccess.jar; D:\environment\java\java_update\jre\lib\ext\jfxrt.jar; D:\environment\java\java_update\jre\lib\ext\localedata.jar; D:\environment\java\java_update\jre\lib\ext\nashorn.jar; D:\environment\java\java_update\jre\lib\ext\sunec.jar; D:\environment\java\java_update\jre\lib\ext\sunjce_provider.jar; D:\environment\java\java_update\jre\lib\ext\sunmscapi.jar; D:\environment\java\java_update\jre\lib\ext\sunpkcs11.jar; D:\environment\java\java_update\jre\lib\ext\zipfs.jar; D:\environment\java\java_update\jre\lib\javaws.jar; D:\environment\java\java_update\jre\lib\jce.jar; D:\environment\java\java_update\jre\lib\jfr.jar; D:\environment\java\java_update\jre\lib\jfxswt.jar; D:\environment\java\java_update\jre\lib\jsse.jar; D:\environment\java\java_update\jre\lib\management-agent.jar; D:\environment\java\java_update\jre\lib\plugin.jar; D:\environment\java\java_update\jre\lib\resources.jar; D:\environment\java\java_update\jre\lib\rt.jar; D:\environment\java_project\jdbc\out\production\Basic grammar; D:\environment\java_project\javase\Basic Syntax\src\com\lib\commons-io-2.6.jar; D:\environment\IntelliJ IDEA 2021.2.1\lib\idea_rt.jar */ } }
Get the complete structure of the runtime class
Getting the complete structure of the runtime class through reflection
Field,Method,Constructor,Superclass,Interface,Annotation
- All interfaces implemented
- Inherited parent
- All Constructors
- All methods
- All Field s
- annotation
- . . .
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; //Get class information public class Test08 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class c1 = Class.forName("com.annotation_reflection.reflection.User"); User user = new User(); c1 = user.getClass(); //Get the name of the class System.out.println(c1.getName());//Get Package Name + Class Name System.out.println(c1.getSimpleName());//Get Class Name //Get the properties of the class System.out.println("==============================================="); Field[] fields = c1.getFields();//Only public properties can be found fields = c1.getDeclaredFields();//Find all attributes for (Field field : fields) { System.out.println(field); } //Gets the value of the specified property Field name = c1.getDeclaredField("name"); System.out.println(name); //Methods to get classes System.out.println("==============================================="); Method[] methods = c1.getMethods();//Get all public methods for this class and its parent for (Method method : methods) { System.out.println("Natural:"+method); } methods = c1.getDeclaredMethods();//Get all methods of this class for (Method method : methods) { System.out.println("getDeclaredMethods:"+method); } //Get the specified method //heavy load Method getName = c1.getMethod("getName",null); Method setName = c1.getMethod("setName",String.class); System.out.println(getName); System.out.println(setName); //Gets the specified constructor System.out.println("======================================================"); Constructor[] constructors = c1.getConstructors();//Get the public constructor for (Constructor constructor : constructors) { System.out.println(constructor); } constructors = c1.getDeclaredConstructors();//Get all constructors for this class for (Constructor constructor : constructors) { System.out.println(constructor); } //Gets the specified constructor Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); System.out.println("Appoint:"+declaredConstructor); } }
What can I do with a Class object?
- Object that creates a class: Call the newInstance() method of the Class object
- 1) Class must have a parameterless constructor
- 2) The class constructor needs sufficient access
Reflection? Can't an object be created without a parameterless constructor? The operation can be instantiated as long as the constructor in the class is explicitly called during the operation and the parameters are passed in.
- The steps are as follows:
- 1) Get the constructor of the specified parameter type of the class through getDeclatedConstructor (Class... ParameterTypes)
- 2) Pass an array of objects into the constructor's parameters, which contain the parameters required in the constructor
- 3) Instantiate objects through Constructor
Call the specified method
By reflection, the method in the class is invoked, and done by the Method class
- Get a Method object through the getMethod(String name,Class...parameterTypes) method of the Class class and set the type of parameters required for this method to operate.
- The call is then made using Object invoke (Object obj, Object[] args) and the parameter information of the obj object to be set is passed to the method
Object invoke(Object obj, Object...args)
- Object returns the value of the original method, null if the original method has no return value
- The parameter Object obj can be null if the original method is static
- Object[] args is null if the original method parameter list is empty
- If the original method is declared private, you need to explicitly call the setAccessible(true) method of the method object before calling this invoke() method, which will access the privates method
setAccessible
- Method and Field, Constructor objects all have setAccessible() methods
- setAccessible is the switch that starts and disables access security checks
- A parameter value of true indicates that the reflected object should cancel the Java language access check when used
- Improve the efficiency of reflection. Set to true if reflection is required in the code and the sentence code needs to be called frequently
- Make private members that are otherwise inaccessible accessible
- A parameter value of false indicates that the reflected object should perform Java language access checks
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; //Create objects dynamically by reflection public class Test09 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //Get Class Object Class c1 = Class.forName("com.annotation_reflection.reflection.User"); //Construct an object //User user = (User) c1.newInstance();// Essentially, the parameterless constructor of the class is called //System.out.println(user); //Create Objects Through Constructors //Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class); //User user2 = (User)constructor.newInstance("Qin Xinjiang", 001,18); //System.out.println(user2); //Call common methods through reflection User user3 = (User)c1.newInstance(); //Get a method by reflection Method setName = c1.getDeclaredMethod("setName", String.class); //invoke: meaning of activation //(object, ""value of method") setName.invoke(user3,"Crazy"); System.out.println(user3.getName()); //Operating attributes through reflection System.out.println("=============================================================="); User user4 = (User)c1.newInstance(); Field name = c1.getDeclaredField("name"); //Private attributes cannot be manipulated directly. We need to turn off security detection for programs, setAccessible(true) for attributes or methods. name.setAccessible(true);// name.set(user4,"Crazy 2"); System.out.println(user4.getName()); } }
Performance comparison of different execution methods
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; //Analyzing performance issues public class Test10 { //Normal Call public static void test01(){ User user = new User(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { user.getName(); } long endTime = System.currentTimeMillis(); System.out.println("Ordinary execution 1 billion times:"+(endTime-startTime)+"ms"); } //Reflection Call public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User(); Class c1 = user.getClass(); Method getName = c1.getDeclaredMethod("getName",null); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { getName.invoke(user,null); } long endTime = System.currentTimeMillis(); System.out.println("Reflective execution 1 billion times:"+(endTime-startTime)+"ms"); } //Reflection Call Close Detection public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User(); Class c1 = user.getClass(); Method getName = c1.getDeclaredMethod("getName",null); getName.setAccessible(true); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { getName.invoke(user,null); } long endTime = System.currentTimeMillis(); System.out.println("Turn off detection 1 billion times:"+(endTime-startTime)+"ms"); } public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { test01(); test02(); test03(); } }
Reflection Operation Generics
- Java uses a generic erase mechanism to introduce generics. Generics in Java are used only by the compiler javac to ensure data security and avoid cast problems. However, once compiled, all generic-related types are erased
- To manipulate these types through reflection, Java has added several new types, ParameterizedType, GenericArrayType, TypeVariable, and WildcardType, to represent types that cannot be normalized into a Class class but are aligned with the original type
- ParameterizedType: Represents a parameterized type, such as Collection
- GenericArrayType: Indicates that an element type is a parameterized type or an array type of type variables
- TypeVariable: is the common parent interface for various types of variables
- WilecardType: Represents a wildcard type expression
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; //Practice Reflection Operation Generics public class Test11 { public void Test01(Map<String,User> map, List<User> list){ System.out.println("test01"); } public Map<String,User> test02(){ System.out.println("test02"); return null; } public static void main(String[] args) throws NoSuchMethodException { Method method = Test11.class.getMethod("test01", Map.class, List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("#"+genericParameterType); if (genericParameterType instanceof ParameterizedType){ Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } method = Test11.class.getMethod("test02",null); Type genericReturnType = method.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType){ Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } }
Reflection operation notes
- getAnnotations
- getAnnotation
import java.lang.annotation.*; import java.lang.reflect.Field; //Practice reflex operation notes public class Test12 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class c1 = Class.forName("com.annotation_reflection.reflection.Student2"); //Obtain annotations through reflection Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //Get the value of the comment Tablekuang tablekuang = (Tablekuang)c1.getAnnotation(Tablekuang.class); String value = tablekuang.value(); System.out.println(value); //Get annotations specified by the class Field f = c1.getDeclaredField("name"); Fieldkuang annotation = f.getAnnotation(Fieldkuang.class); System.out.println(annotation.columnName()); System.out.println(annotation.type()); System.out.println(annotation.length()); } } @Tablekuang("db_student") class Student2{ @Fieldkuang(columnName = "db_id",type = "int",length = 10) private int id; @Fieldkuang(columnName = "db_age",type = "int",length = 10) private int age; @Fieldkuang(columnName = "db_name",type = "varchar",length = 3) private String name; public Student2(){ } public Student2(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student2{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}'; } } //Comment on class name @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Tablekuang{ String value(); } //Annotation of attributes @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface Fieldkuang{ String columnName(); String type(); int length(); }