Java core technology - exception, assertion and log

When a program error occurs, do at least the following:

* Notify users of errors

* Save all work results

* Allow the user to exit the program in a graceful manner

Java handles exceptions using an error-catching mechanism called exception handling.

The first part of this chapter covers Java's exceptions, the second part describes how to use assertions to selectively enable detection, and the third part discusses the standard Java logging framework.

1.1 Handling errors

Suppose an error occurs during the execution of a Java program (file contains error message, network connection problem, invalid array subscript, attempt to use an object reference that has not been assigned a value), the program should:

* Return to a safe state and be able to let the user execute some other commands

* Allows the user to save the results of all operations and terminate the program in a graceful manner

Often code that detects error conditions is far from code that can restore data to a safe state or save the results of user actions and exit gracefully. The job of exception handling is to transfer control from where the error occurred to an error handler capable of handling the situation.

1.2 Anomaly classification

In Java, exception objects are derived from an instance of the Throwable class.

All exceptions are inherited from Throwable, but are decomposed into two branches at the next level: Error and Exception

The Error class hierarchy describes internal errors and resource exhaustion errors of the Java runtime system.

Exception is decomposed into two branches:

1. Exceptions caused by program errors belong to RuntimeException:

* wrong type conversion

*Array access out of bounds

* access to null pointer

2. There is no problem with the program itself, but exceptions caused by problems such as I/O errors belong to other exceptions.

* Attempt to exempt read data at end of file

* Attempted to open a file that does not exist

* Attempts to find a Class object based on the given string, and the class represented by this string does not exist

"If there is a RuntimeException, it must be your problem" is a fairly reasonable rule.

ArrayIndexOutOfBoundsException should be avoided by detecting whether the array index is out of bounds;

NullPointerException should be prevented by checking for null before using the variable

The Java Language Specification refers to all exceptions derived from the Error class or RuntimeException class as unchecked exceptions , and all other exceptions as checked exceptions .

The compiler will check that exception handlers are provided for all checked exceptions.

1.2 Declaring checked exceptions

Java methods can throw an exception if an unhandled situation is encountered.

A method should declare all possible exceptions (throws XXException) in its header

An exception should be thrown when the following 4 conditions are encountered:

1. Call a method that throws the checked exception, such as the FileInputStream constructor

2. An error is found during the running of the program, and a checked exception is thrown using the throw statement

3. The program fails and an unchecked exception is thrown

4. Internal Errors in the Java Virtual Machine and Runtime Libraries

For the first two cases, the programmer calling the method must be told that an exception may be thrown.

The latter two cases do not need to be declared in the method header

Summary: A method must declare all checked exceptions that may be thrown , and unchecked exceptions are either uncontrollable or should be avoided.

If a method of the superclass is overridden in the subclass, the checked exception declared in the subclass method cannot be more general than the exception declared in the superclass method, and if no exception is thrown in the superclass, the subclass also boons that Throw any exception.

If a method in a class is declared to throw an exception, and the exception is an instance of a particular class, then the method may throw an exception of that class or an exception of any subclass of this class.

1.3 How to throw exceptions

1. Find a suitable exception class

2. Create an object of this class

3. Throw the object

1.4 Create exception class

In a program, you may encounter problems that are not adequately described by any of the standard exception classes. In this case, you need to create your own exception class.

All we need to do is to define a class derived from the Exception class or a subclass of Exception. By convention, the defined class should contain two constructors, a default constructor and a constructor with detailed description information.

class FileFormatException extends IOException
{
    public FileFormatException(){}
    public FileFormatException(String gripe)
    {
        super(gripe);
    }
}

2 catch exception

If an exception occurs without being caught anywhere, the program terminates execution.

In general, you should catch those exceptions that you know how to handle, and pass those exceptions that you don't know how to handle.

It is not allowed to appear in the throws specifier of a subclass beyond the range of exception classes listed in the superclass method.

2.1 Catching exceptions

Use try/catch statement to catch exceptions in Java

2.2 Catch multiple exceptions

2.3 Rethrowing exceptions and exception chains

An exception can be thrown in the catch clause, the purpose of which is to change the type of the exception.

try
{
    access the database
}
catch(SQLException e)
{
    Throwable se=new ServletException("database error");
    se.initCause(e);
    throw se;
}

When an exception is caught, the original exception can be retrieved with the following statement:

Throwable e=se.getCause();

This packaging technique is highly recommended. This allows the user to throw advanced exceptions in the subsystem without losing the details of the original exception.

Weird tricks:

Wrapping techniques are useful if a checked exception occurs in a method and it is not allowed to throw it. We can catch this checked exception and wrap it into a runtime exception.

2.4 finally clause

When the code throws an exception, it terminates the processing of the rest of the code in the method, thus potentially creating resource recovery issues.

There is a solution in Java, which is the finally clause. Regardless of whether an exception is caught or not, the code in the finally clause is executed.

A try statement can have a finally clause without a catch clause.

It is strongly recommended to decouple try/catch and try/finally blocks. This improves the clarity of the code (doesn't solve the case where try and close throw exceptions at the same time):

InputStream in=...;
try
{
    try
    {
        code that might throw exception
    }
    finally
    {
        in.close();
    }
}
catch(IOException e)
{
    show error message
}

The inner try block has only one responsibility, which is to ensure that the input stream is closed. The outer try block also has only one responsibility, which is to ensure that errors are reported. This design is not only clear, but will also report errors in the finally clause.

An unexpected result occurs when a finally clause contains a return statement:

public static int f(int n)
{
    try
    {
        int r=n*n;
        return r;
    }
    finally
    {
        if(n==2) return 0;
    }
}

The contents of the finally clause will be executed before the method returns. If there is also a return statement in the finally clause, this value will override the original return value. That is, the return value of the above code calling f(2) is 0.

2.5 try statement with resource

The simplest form of try-with-resources is (assuming the resource belongs to a class that implements the AutoCloseable interface):

try(Resource res=...)
{
    work with res
}

When the try block exits, res.close() is automatically called.

If the try block throws an exception, and the close method also throws an exception, the try statement with the resource handles the situation nicely -- the exception in the try is rethrown, and the exception thrown by the close method will " suppressed".

2.6 Analyzing stack trace elements

A stack trace is a list of method invocations that contain specific locations of method invocations during program execution.

Using the getStackTrace method, it gets an array of StackTraceElement objects that can be analyzed in your program:

Throwable t=new Throwable();
StackTraceElement[] frames=t.getStackTrace();
for(StackTraceElement frame:frames)
    analyze frame

3 Tips for Using Exception Mechanisms

A few tips for using the exception mechanism:

1. Exception handling is no substitute for simple testing

Catching exceptions takes significantly longer than executing simple tests, so the basic rule is - only use the exception mechanism in exceptional cases.

2. Don’t over-refine exceptions

Separate normal handling from error handling

3. Leverage Exception Hierarchy

Don't hesitate to convert one exception to another more appropriate exception

4. Don't suppress exceptions

5. When it comes to detecting errors, being “harsh” is better than letting go

For example, whether Stack.pop returns a null when the stack is empty, or throws an exception, we think the latter is better.

6. Don’t be shy about passing exceptions

Passing exceptions is better than catching them


 

4 Using assertions

In a self-preserving program, assertions are often used.

4.1 The concept of assertions

The assertion mechanism allows some checking statements to be inserted into the code during testing. These inserted check statements will be automatically removed when the code is released.

The Java language introduces the keyword assert. There are two forms:

1.assert condition;

2.assert condition: expression;

Both forms will test the condition and throw an AssertionError if the result is false. In the second form, the expression will be passed into the AssertionError constructor and converted into a message string.

4.2 Enabling and disabling assertions

Assertions are disabled by default.

It can be enabled programmatically with the -enableassertions or -ea (enable all class assertions in the default package) option:

java -enableassertions MyApp

Disable assertions with -disableassertions or -da

4.3 Parameter checking using assertions

In Java, 3 mechanisms are given to handle system errors:

* throw an exception

*log

* use assertions

When to use assertions:

* Assertion failure is a fatal, unrecoverable error

*Assertion checking is only used in development and testing phases

Assertions should only be used during the testing phase to determine the location of errors inside the program.

4.4 Using assertions for documentation assumptions

Assertions are a tactical tool used during the testing and debugging phase; logging is a tactical tool used throughout the life of a program.


 

5 Logging

The logging API is used to help observe the operation of the program running:

* Can easily cancel all logging, or just a certain level of logging, and it's easy to turn this on and off

* It is easy to suppress the output of logging, so there is little overhead for this logging code to stay in the program

* Log records can be directed to different processors for display in the console, for storage in files, etc.

* Both loggers and processors can filter records. The filter can discard those useless entries according to the criteria specified by the filter implementer

*Log records can be formatted in different ways, e.g. plain text or XML

* An application can use multiple loggers with hierarchical names like package names, for example, com.mycompany.myapp

*By default, the configuration of the logging system is provided by the configuration file. Applications can override this configuration if needed

5.1 Basic log

"Virtual Log"

To generate simple logging, you can use the global logger and call its info method:

Logger.getGlobal().info("File->Open menu item selected");

Invoke where appropriate:

Logger.getGlobal().setLevel(Level.OFF);

will cancel all logs

5.2 Advanced Logs

"Enterprise Log"

In a professional application, instead of logging all the logs to a global logger, you can customize the logger

You can call the getLogger method to create or get a logger:

private static final Logger myLogger=Logger.getLogger("com.mycompany.myapp");

A logger that is not referenced by any variable may be garbage collected, to prevent this from happening, use a static variable to store a reference to the logger

Typically, there are the following 7 logger levels:

*SEVERE

*WARNING

*INFO

*CONFIG

*FINE

*FINER

*FINEST

By default, only the first three levels are logged

logger.setLevel(Level.FINE);

Both FINE and higher level logging can now be logged

Level.ALL turns on the recording of all levels, Level.OFF turns off the recording of all levels

For all levels there are the following logging methods:

logger.warning(message);

logger.fine(message);

logger.log(Level.FINE,message);

The default logging will show the class name and method name containing the log call, if the virtual machine has optimized the execution process, you can use the logp method to get the exact location of the calling class and method:

void logp(Level l,String className,String methodName,String message)

A method to trace the execution flow (will generate FINER level and log records starting with the strings ENTRY and RETURN):

void entering(String className,String methodName,Object[] params)

void exiting(String className,String methodName,Object result)

A common use of logging is to record those unexpected exceptions:

void throwing(String className, String methodName, Throwable t) - FINER level record and a message starting with THROW

void log(Level l,String message,Throwable t)

5.3 Modify the log manager configuration

Various properties of the logging system can be modified by editing the configuration file. By default, configuration files exist in:

jre/lib/logging.properties

To use another configuration file:

1. Set the java.util.logging.config.file property as the storage location of the configuration file and use the command java -Djava.util.logging.config.file=configFile MainClass

2. Call System.setProperty("java.util.logging.config.file",file) in main, and also call LogManager.readConfiguration() to reinitialize the log manager.

You can specify your own logging level by com.mycompany.myapp.level=FINE

The handler level can be set via java.util.logging.ConsoleHandler.level=FINE

5.4 Localization

We may want to localize the log message so that it can be read by users around the world.

Localized applications contain resource bundles, for example, a resource bundle might map the string "readingFile" to English "Reading file".

Each resource bundle has a name (eg com.mycompany.logmessages)

When requesting a logger, you can specify a resource bundle:

Logger logger = Logger.getLogger (loggerName, "com.mycompany.logmessages")

Then, specify the resource bundle keyword for the log message instead of the actual log message string:

logger.info("readingFile")

The message should contain placeholders {0}, {1}, like:

Reading file {0}

Then call one of the following methods to pass a specific value to the placeholder:

logger.log(Level.INFO,"readingFile",fileName);

5.5 Processor

By default, the logger sends records to the ConsoleHandler, which outputs it to the System.err stream. In particular, the logger also sends records into the parent processor.

Like loggers, processors also have logging levels. For a log record to be logged, its logging level must be higher than the logger and handler thresholds.

The logging level for the default console handler set by the log manager configuration file is:

java.util.logging.ConsoleHandler.level=INFO

To log at the FINE level, you must modify the default logging level and processor level in the configuration file. Or you can bypass the configuration file and install your own processor:

Logger logger=Logger.getLogger("com.mycompany.myapp");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(false);
Handler handler=new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);

By default, the logger sends records to its own handler and parent handler, so setting the useParentHandlers property to false will not see these records twice.

To send log records elsewhere, additional handlers must be added. The logging API provides two useful handlers, a FileHandler and a SockerHandler.

SockerHandler sends records to a specific host and port. FileHandler can collect records from files.

The default behavior of the file processor can be modified by setting different parameters in the log manager configuration file.

5.6 Filters

By default, the filter is based on the level of logging.

Filters can be customized by implementing the Filter interface and defining the following methods:

boolean isLoggable(LogRecord record)

To install a filter into a logger or handler, simply call the setFilter method.

Note that there can only be at most one filter at a time.

5.7 Formatter

The ConsoleHandler and FileHandler classes can generate log records in text and XML format. But you can also customize the format, here you need to extend the Formatter class and override the following methods:

String format(LogRecord record)

Finally, call the setFormatter method to install the formatter into the processor.

5.8 Logging Description

Logging common operations:

1. For a simple application, choose a logger and name the logger the same as the main application package (com.mycompany.myprog)

Call the following method to get the logger:

private static final Logger logger=Logger.getLogger("com.mycompany.myprog");

2. The default log configuration records all messages with a level equal to or higher than the INFO level to the console. You can modify the default configuration file or install a new default configuration.

3. Messages with levels INFO, WARNING, and SEVERE will all be displayed on the console, so it is best to set these levels only for messages that are meaningful to program users. Set the logging desired by the programmer to the FINE level.

6 Debugging Tips

1. You can print or log the value of any variable:

System.out.println("x="+x)或Logger.getGlobal().info("x="+x)

2. Place a main method in each class so that each class can be unit tested.

3.JUnit is a very common unit testing framework, learn and use it

4. The log proxy is an object of a subclass that can intercept method calls, log and then call methods in the superclass.

A proxy object can be created as an instance of an anonymous subclass:

Random generator=new 
    Random()
    {
        public double nextDouble()
        {
            double result=super.nextDouble();
            Logger.getGlobal().info("nextDouble:"+result);
            return result;
        }
}

5. Using the printStackTrace method provided by the Throwable class, the stack status can be obtained from any exception object.

It is not necessary to generate stack traces by catching exceptions. The stack trace can be obtained by inserting the following statement anywhere in the code:

Thread.dumpStack();

6. In general, the stack trace is displayed on System.err, and it can also be sent to a file using the printStackTrace(PrintWriter s) method.

7. Log uncaught exceptions to a file:

Thread.setDefaultUncaughtExceptionHanler()

8. To view the class loading process, you can use the -verbose flag to start the Java virtual machine

9. The -Xlint option tells the compiler to check for some common code problems

10. Use jconsole's graphical tools

11. Use jmap to view heap dumps

12. Running the virtual machine with the -Xprof flag can run a profiler to trace frequently called methods in the code. The output also shows which methods were compiled by the just-in-time compiler.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325223868&siteId=291194637