Java编程思想之十二 通过异常处理错误
Java的基本概念是结构不佳的代码不能运行余下的问题必须在运行期间解决,这就需要错误源能通过某种方式,把适当的信息传递给某个接收者——该接收者将知道如何正确处理这里问题。
12.1 概念
使用异常所带来的另一个相当明显的好处,它往往能够降低错误处理代码的复杂度。
12.2 基本异常
异常情形是指阻止当前方法或作用域继续执行的问题。把异常情形与普通问题相区分很重要,普通问题是指,在当前环境下能得到足够的信息,总能处理这个错误。而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解决问题。你所能做的就是从当前环境跳出,并且把问题提交给上一级环境。这就是抛出异常所发生的事情。
当抛出异常后,有几件事就会发生。首先,同Java中其他对象的创建一样,将使用new在堆上创建异常对象,然后,当前的执行路径被终止,并且从当前环境中弹出堆异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方来继承执行程序。这个恰当的地方就是异常处理程序,它的任务就是将程序从错误状态中恢复,以使程序能要么换一种方式运行,要么继承运行下去。
12,2,1 异常参数
我们总用new在堆上创建异常对象,这也伴随着存储控件的分配和构造器的调用。所有标准异常类都有两个构造器:
- 默认构造器
- 接受字符串作为参数,把相关信息放入异常对象的构造器。
使用new创建了异常对象之后,此对象的引用将传给throw。
可以简单把异常处理看成一种不同的返回机制。抛出异常的方法从当前的作用域退出。将返回一个异常对象,然后退出方法作用域。
12.3 捕获异常
异常如何被捕获,必须首先理解监控区域。
12.3.1 try块
异常处理机制,可以把所有动作都放在try块中,然后再一个地方处理捕获的异常就可以了。
12.3.2 异常处理程序
抛出的异常必须在某处得到处理。这个地点就是异常处理程序。异常处理程序紧跟在try块后,以关键字catch表示。
当异常抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。
终止与恢复
异常处理理论有两种基本模型:
- Java支持终止模型:假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行,一旦异常被抛出,就表明错误已无法挽回,也不能会来继续执行。
- 恢复模式:异常处理程序的工作就是修正错误,然后重新尝试调用出问题的放大,并认为第二次能够成功。
恢复模型会导致耦合:恢复性的处理程序需要了解异常抛出的地点,着势必要包含依赖于抛出位置的非通用性代码。这增加了代码编写和维护的困难。
12.4 创建自定义异常
要自己定义异常,必须从已有的异常类继承,最好选择意思相近的异常类继承。
public class InheritingExceptions
{
public void f() throws SimpleException
{
System.out.println("Throw SimpleException from f()");
throw new SimpleException();
}
public static void main(String[] args)
{
InheritingExceptions sed = new InheritingExceptions();
try
{
sed.f();
}
catch (SimpleException e)
{
System.out.println("Caught it!");
}
}
}
class SimpleException extends Exception
{
}
编译器创建了默认构造器,它将自动调用基类的默认构造器。
public class FullConstructors
{
public static void f() throws MyException{
System.out.println("Throwing MyExcetion from f()");
throw new MyException();
}
public static void g() throws MyException{
System.out.println("Throwing MyExcetion from g()");
throw new MyException("Originated in g()");
}
public static void main(String[] args){
try{
f();
}
catch (MyException e){
e.printStackTrace(System.out);
}
try{
g();
}
catch (MyException e){
e.printStackTrace(System.out);
}
}
}
class MyException extends Exception{
public MyException(){}
public MyException(String msg){super(msg);}
}
12.4.1 异常与记录日志
使用基本日志记录功能
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;
public class LoggingExceptions
{
public static void main(String[] args){
try{
throw new LoggingException();
}
catch (LoggingException e){
System.out.println("Caught "+e);
}
try{
throw new LoggingException();
}
catch (LoggingException e){
System.out.println("Caught "+e);
}
}
}
class LoggingException extends Exception{
private static Logger logger= Logger.getLogger("LoggingException ");
public LoggingException(){
StringWriter trace=new StringWriter();
printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());
}
}
需要捕获或记录他人编写的异常,必须在异常处理程序中生成日志消息
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;
public class LoggingExceptions2
{
private static Logger logger=Logger.getLogger("LoggingExceptions2");
static void logException(Exception e){
StringWriter trace=new StringWriter();
e.printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());
}
public static void main(String[] args){
try{
throw new NullPointerException();
}
catch(NullPointerException e){
logException(e);
}
}
}
更进一步自定义异常,加入额外的构造器和成员
public class ExtraFeatures
{
public static void f() throws MyException2{
System.out.println("Throwing MyException2 from f()");
throw new MyException2();
}
public static void g() throws MyException2{
System.out.println("Throwing MyException2 from g()");
throw new MyException2("Originated in g()");
}
public static void h() throws MyException2{
System.out.println("Throwing MyException2 from h()");
throw new MyException2("Originated in g()",47);
}
public static void main(String[] args){
try{
f();
}
catch (MyException2 e){
e.printStackTrace(System.out);
}
try{
g();
}
catch (MyException2 e){
e.printStackTrace(System.out);
}
try{
h();
}
catch (MyException2 e){
e.printStackTrace(System.out);
System.out.println("e.val()="+e.val());
}
}
}
class MyException2 extends Exception{
private int x;
public MyException2(){}
public MyException2(String msg){super(msg);}
public MyException2(String msg,int x){super(msg);this.x=x;}
public int val(){return x;}
public String getMessage(){
return "Detail message:"+x+" "+super.getMessage();
}
}
异常也是一种对象,所有可以继续修改异常类,获得更强大的功能。
12.5 异常说明
Java提供语法,使可以告知客户端程序员某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。这就是异常说明,它属于方法说明的一部分,紧跟在形式参数之后。
异常说明使用了附加的关键字throws,后面接一个潜在的异常类型的列表,所以方法定义可能看起来像这样:
void f() throws TooBig,TooSmall
{
}
如果方法里的代码产生了异常却没有处理,编译器会发现这个问题并且提醒你,要么处理这个异常,要么就在异常说明中表明此方法将产生异常。
这种编译时被强制检查的异常称为被检查的异常。
12.6 捕获所有异常
gillInStackTrace()用于在Throwable对象的内部记录栈帧的当前状态。这在程序重新抛出错误或异常时很有用。
12.6.1 栈轨迹
printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中,每个元素表示栈中的一贞。
public class WhoCalled
{
static void f(){
try{
throw new Exception();
}
catch (Exception e){
for(StackTraceElement ste:e.getStackTrace())
System.out.println(ste.getMethodName());
}
}
static void g(){f();}
static void h(){g();}
public static void main(String[] args){
f();
System.out.println("--------------");
g();
System.out.println("--------------");
h();
}
}
12.6.2 重新抛出异常
重新抛出异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch子句将被忽略。异常对象的所有信息都得以保持,所以高一级环境中捕获此异常的处理程序可以从这个异常对象中得到所有信息。
public class Rethrowing
{
public static void f() throws Exception{
System.out.println("originating the ");
throw new Exception("thrown from f()");
}
public static void g() throws Exception{
try{
f();
}
catch (Exception e){
System.out.println("Inside g(),e.printStackTrace()");
e.printStackTrace(System.out);
throw e;
}
}
public static void h() throws Exception{
try{
f();
}catch (Exception e){
System.out.println("Inside h(),e.printStackTrace()");
e.printStackTrace(System.out);
throw (Exception) e.fillInStackTrace();
}
}
public static void main(String[] args){
try{
g();
}catch (Exception e){
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
try{
h();
}catch (Exception e){
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
}
}
有可能在捕获异常之后抛出另一种异常。这么做的话,得到的效果类似于使用fillInStackTrace(),有关原来异常发生点的信息会丢失,剩下的是于新的跑出点有关的信息:
public class RethrowNew
{
public static void f() throws OneException{
System.out.println("originating the exception");
throw new OneException("thrown from f()");
}
public static void main(String[] args){
try{
try{
f();
}catch (OneException e){
System.out.println("Caught in inner try,e.printStackTrace");
e.printStackTrace(System.out);
throw new TwoException("from inner try");
}
}catch (TwoException e){
System.out.println("Caught in inner try,e.printStackTrace");
e.printStackTrace(System.out);
}
}
}
class OneException extends Exception{
public OneException(String s){super(s);}
}
class TwoException extends Exception{
public TwoException(String s){super(s);}
}
异常都是用new在堆上创建的对象,所有垃圾回收器会自动把它们清理掉。
12.6.3 异常链
想在捕获一个异常后抛出另一个异常,并且希望把原始异常信息保持下来,这被称为异常链。现在所有Throwable的子类在构造器中都可以接受一个causr对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链最终到最初发生的位置。
class DynamicFieldsException extends Exception {}
public class DynamicFields {
private Object[][] fields;
public DynamicFields(int initialSize) {
fields = new Object[initialSize][2];
for(int i = 0; i < initialSize; i++)
fields[i] = new Object[] { null, null };
}
public String toString() {
StringBuilder result = new StringBuilder();
for(Object[] obj : fields) {
result.append(obj[0]);
result.append(": ");
result.append(obj[1]);
result.append("\n");
}
return result.toString();
}
private int hasField(String id) {
for(int i = 0; i < fields.length; i++)
if(id.equals(fields[i][0]))
return i;
return -1;
}
private int
getFieldNumber(String id) throws NoSuchFieldException {
int fieldNum = hasField(id);
if(fieldNum == -1)
throw new NoSuchFieldException();
return fieldNum;
}
private int makeField(String id) {
for(int i = 0; i < fields.length; i++)
if(fields[i][0] == null) {
fields[i][0] = id;
return i;
}
// No empty fields. Add one:
Object[][] tmp = new Object[fields.length + 1][2];
for(int i = 0; i < fields.length; i++)
tmp[i] = fields[i];
for(int i = fields.length; i < tmp.length; i++)
tmp[i] = new Object[] { null, null };
fields = tmp;
// Recursive call with expanded fields:
return makeField(id);
}
public Object
getField(String id) throws NoSuchFieldException {
return fields[getFieldNumber(id)][1];
}
public Object setField(String id, Object value)
throws DynamicFieldsException {
if(value == null) {
// Most exceptions don't have a "cause" constructor.
// In these cases you must use initCause(),
// available in all Throwable subclasses.
DynamicFieldsException dfe =
new DynamicFieldsException();
dfe.initCause(new NullPointerException());
throw dfe;
}
int fieldNumber = hasField(id);
if(fieldNumber == -1)
fieldNumber = makeField(id);
Object result = null;
try {
result = getField(id); // Get old value
} catch(NoSuchFieldException e) {
// Use constructor that takes "cause":
throw new RuntimeException(e);
}
fields[fieldNumber][1] = value;
return result;
}
public static void main(String[] args) {
DynamicFields df = new DynamicFields(3);
System.out.print(df);
try {
df.setField("d", "A value for d");
df.setField("number", 47);
df.setField("number2", 48);
System.out.print(df);
df.setField("d", "A new value for d");
df.setField("number3", 11);
System.out.print("df: " + df);
System.out.print("df.getField(\"d\") : " + df.getField("d"));
Object field = df.setField("d", null); // Exception
} catch(NoSuchFieldException e) {
e.printStackTrace(System.out);
} catch(DynamicFieldsException e) {
e.printStackTrace(System.out);
}
}
}
Java标准异常
Throwable这个Java类被用来表示任何可以作为异常抛出的类。Throwable对象可分为两中类型:Error用来表示编译时和系统错误。Exception是可以被抛出的基本类型。
RuntimeException类型的异常也许会穿越所有执行路径直达main()方法,而不会被捕获。
public class NeverCaught
{
static void f(){
throw new RuntimeException("From f()");
}
static void g(){
f();
}
public static void main(String[] args){
g();
}
}
RuntimeException代表的是编程错误:
- 无法预料的错误
- 作为程序员,应该在代码中检查的错误
应该把异常机制用来处理一些烦人的运行时错误,这些错误往往是由代码控制能力之外的因素导致的。
12.8 使用finally进行清理
对于一些代码,可能会希望无论try中的异常是否抛出,都能得到执行。可以使用finally子句。
public class FinallyWorks
{
static int count=0;
public static void main(String[] args){
while(true){
try{
if(count++==0)
throw new ThreeException();
System.out.println("No exception");
}
catch (ThreeException e){
System.out.println("ThreeException");
}
finally
{
System.out.println("finally cluse "+count);
if(count==2)break;
}
}
}
}
class ThreeException extends Exception{}
12.8.1 finally用来做什么
当要把除内存之外的资源恢复到初始状态时,就要用到finally子句
public class WithFinally
{
static Switch sw = new Switch();
public static void main(String[] args) {
try {
sw.on();
// Code that can throw exceptions...
OnOffSwitch.f();
} catch(OnOffException1 e) {
System.out.println("OnOffException1");
} catch(OnOffException2 e) {
System.out.println("OnOffException2");
} finally {
sw.off();
}
}
}
class Switch {
private boolean state = false;
public boolean read() { return state; }
public void on() { state = true; System.out.print(this); }
public void off() { state = false; System.out.print(this); }
public String toString() { return state ? "on" : "off"; }
}
class OnOffSwitch {
private static Switch sw = new Switch();
public static void f()
throws OnOffException1,OnOffException2 {}
public static void main(String[] args) {
try {
sw.on();
// Code that can throw exceptions...
f();
sw.off();
} catch(OnOffException1 e) {
System.out.println("OnOffException1");
sw.off();
} catch(OnOffException2 e) {
System.out.println("OnOffException2");
sw.off();
}
}
}
class OnOffException1 extends Exception {}
class OnOffException2 extends Exception {}
当设计break和continue语句时,finally也会得到执行
12.8.2 在return中使用finally
因为finally子句总会执行,所以在一个方法中,可以从多个点返回,并且可以保证重要的清理工作仍旧会执行:
public class MultipleReturns
{
public static void f(int i){
System.out.println("Initialization that requires");
try{
System.out.println("Point 1");
if (i==1)return;
System.out.println("Point 2");
if (i==2)return;
System.out.println("Point 3");
if (i==3)return;
}
finally
{
System.out.println("end");
}
}
public static void main(String[] args){
for(int i=1;i<=3;i++)
f(i);
}
}
12.8.3 遗憾:异常丢失
public class LoseMessage
{
void f() throws VeryImportantException {
throw new VeryImportantException();
}
void dispose() throws HoHumException {
throw new HoHumException();
}
public static void main(String[] args) {
try {
LoseMessage lm = new LoseMessage();
try {
lm.f();
} finally {
lm.dispose();
}
} catch(Exception e) {
System.out.println(e);
}
}
}
class VeryImportantException extends Exception {
public String toString() {
return "A very important exception!";
}
}
class HoHumException extends Exception {
public String toString() {
return "A trivial exception";
}
}
前一个异常还没有处理就抛出了下一个异常。
12.9 异常的限制
当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。
class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}
abstract class Inning {
public Inning() throws BaseballException {}
public void event() throws BaseballException {
// Doesn't actually have to throw anything
}
public abstract void atBat() throws Strike, Foul;
public void walk() {} // Throws no checked exceptions
}
class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}
interface Storm {
public void event() throws RainedOut;
public void rainHard() throws RainedOut;
}
public class StormyInning extends Inning implements Storm {
// OK to add new exceptions for constructors, but you
// must deal with the base constructor exceptions:
public StormyInning()
throws RainedOut, BaseballException {}
public StormyInning(String s)
throws Foul, BaseballException {}
// Regular methods must conform to base class:
//! void walk() throws PopFoul {} //Compile error
// Interface CANNOT add exceptions to existing
// methods from the base class:
//! public void event() throws RainedOut {}
// If the method doesn't already exist in the
// base class, the exception is OK:
public void rainHard() throws RainedOut {}
// You can choose to not throw any exceptions,
// even if the base version does:
public void event() {}
// Overridden methods can throw inherited exceptions:
public void atBat() throws PopFoul {}
public static void main(String[] args) {
try {
StormyInning si = new StormyInning();
si.atBat();
} catch(PopFoul e) {
System.out.println("Pop foul");
} catch(RainedOut e) {
System.out.println("Rained out");
} catch(BaseballException e) {
System.out.println("Generic baseball exception");
}
// Strike not thrown in derived version.
try {
// What happens if you upcast?
Inning i = new StormyInning();
i.atBat();
// You must catch the exceptions from the
// base-class version of the method:
} catch(Strike e) {
System.out.println("Strike");
} catch(Foul e) {
System.out.println("Foul");
} catch(RainedOut e) {
System.out.println("Rained out");
} catch(BaseballException e) {
System.out.println("Generic baseball exception");
}
}
}
派生类构造器不能捕获基类构造器抛出的异常。
派生类可以不抛出任何异常,即使它是基类定义的异常。
如果处理的刚好是派生类对象的话,编译器只会强制要求你捕获这个类所抛出的异常,如果将它向上转型成基类,那么编译器就会要求你捕获基类的异常。
在继承中,基类的方法必须出现在派生类里,在继承和覆盖的过程中,某个特定的方法异常说明接口不是变大而是变小——这恰好和类接口在继承时的情形相反。
12.10 构造器
一般在构造器中,会把对象设置成安全的初始状态。如果在构造器内抛出异常,这些清理行为也许就不能正常工作了。就算使用finally每次都执行了清理代码,如果构造器在器执行的中途异常,那么某些对象就没有被创建成功,而这些部分在finally子句中却要被清理。
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class InputFile
{
private BufferedReader in;
public InputFile(String fname) throws Exception
{
try
{
in = new BufferedReader(new FileReader(fname));
// Other code that might throw exceptions
}
catch (FileNotFoundException e)
{
System.out.println("Could not open " + fname);
// Wasn't open, so don't close it
throw e;
}
catch (Exception e)
{
// All other exceptions must close it
try
{
in.close();
}
catch (IOException e2)
{
System.out.println("in.close() unsuccessful");
}
throw e; // Rethrow
}
finally
{
// Don't close it here!!!
}
}
public String getLine()
{
String s;
try
{
s = in.readLine();
}
catch (IOException e)
{
throw new RuntimeException("readLine() failed");
}
return s;
}
public void dispose()
{
try
{
in.close();
System.out.println("dispose() successful");
}
catch (IOException e2)
{
throw new RuntimeException("in.close() failed");
}
}
}
对于构造阶段可能会抛出的异常,并且要求清理的类,最安全的时使用嵌套try子句:
public class Cleanup
{
public static void main(String[] args)
{
try
{
InputFile in = new InputFile("Cleanup.java");
try
{
String s;
int i = 1;
while ((s = in.getLine()) != null)
; // Perform line-by-line processing here...
}
catch (Exception e)
{
System.out.println("Caught Exception in main");
e.printStackTrace(System.out);
}
finally
{
in.dispose();
}
}
catch (Exception e)
{
System.out.println("InputFile construction failed");
}
}
}
对InputFile对象的构造在其自己的try语句块中有效,如果构造失败,将进入外部的catch子句,而dispose()方法不会被调用。如果构造成功,我们肯定要确保对象能够被清理,因此在构造器之后立即创建一个新的try语句。执行清理的finally与内部热try语句块相关联。这种方式中,finally子句在构造失败时不会执行的,而是构造成功时才会被执行。
这种通用的清理惯用法在构造器不抛出任何异常时也应该运用。基本规则是:在创建需要清理的对象之后,立即进入一个try-finally语句中:
class NeedsCleanup
{ // Construction can't fail
private static long counter = 1;
private final long id = counter++;
public void dispose()
{
System.out.println("NeedsCleanup " + id + " disposed");
}
}
class ConstructionException extends Exception
{
}
class NeedsCleanup2 extends NeedsCleanup
{
// Construction can fail:
public NeedsCleanup2() throws ConstructionException
{
}
}
public class CleanupIdiom
{
public static void main(String[] args)
{
// Section 1:
NeedsCleanup nc1 = new NeedsCleanup();
try
{
// ...
}
finally
{
nc1.dispose();
}
// Section 2:
// If construction cannot fail you can group objects:
NeedsCleanup nc2 = new NeedsCleanup();
NeedsCleanup nc3 = new NeedsCleanup();
try
{
// ...
}
finally
{
nc3.dispose(); // Reverse order of construction
nc2.dispose();
}
// Section 3:
// If construction can fail you must guard each one:
try
{
NeedsCleanup2 nc4 = new NeedsCleanup2();
try
{
NeedsCleanup2 nc5 = new NeedsCleanup2();
try
{
// ...
}
finally
{
nc5.dispose();
}
}
catch (ConstructionException e)
{ // nc5 constructor
System.out.println(e);
}
finally
{
nc4.dispose();
}
}
catch (ConstructionException e)
{ // nc4 constructor
System.out.println(e);
}
}
}
Section1相当简单:遵循了在可去除对象之后紧跟try—finally原则。如果对象构造不能失败,就不需要任何catch。在Section2中,为了构造和清理,可以看到具有不能失败的构造器对象可以群组在一起。
Section3展示了如何处理那些具有可以失败的构造器,且需要清理的对象。
异常匹配
抛出异常的时候,异常处理系统会按照代码的书写顺序找出最近的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不在继续查找。
如果把捕获基类的catch子句放在最前面,以此想把派生类的异常全屏蔽,就像这样:
try
{
throw new Sneeze();
}
catch(Annoance a){}
catch(Sneeze s){}
这样编译器就会发现Sneeze的catch子句永远也得不到执行,因此它会向你报错。
12.12 其他可选方式
异常处理系统就像一个活门,使你能放弃程序的正常执行序列。开发异常处理的初衷是为了方便处理错误。
异常处理的一个重要原则:只有在你知道如何处理的情况下才捕获异常。就是把错误的代码同错误发生地点相分离。
被检查的异常:因为它们强制你在可能还没准备好处理错误的时候被迫加上catch子句,这就导致了吞食则有害的问题。
12.12.1 把异常传递给控制台
12.13 异常使用指南
应在下列情况下使用异常:
- 在恰当的级别处理问题。(知道如何处理的情况下捕获异常)
- 解决问题并且重新调用产生异常的方法
- 进行少许修补,然后绕过异常发生的地方继续执行。
- 用别的数据进行计算,以代替方法预计会返回的值。
- 把当前运行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层。
- 把当前运行环境下能做的事情尽量做完,然后把不同的异常重抛到更高层。
- 终止程序
- 进行简化。
- 让类库和程序更安全。
Java编程思想之十二 通过异常处理错误的更多相关文章
- Java编程思想学习(十二) 数组和容器
一.数组 1).数组的多种初始化方式 下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象.值得注意的是:对象数组和普通数组的各种操作基本上都是一样的:要说有什么不同的 ...
- Java编程思想之十八 枚举类型
关键字enum可以将一组具名的值的有限集合创建为一种新的类型, 而这些具名的值可以作为常规的程序组件使用.这是一种非常有用的功能. 18.1 基本enum特性 创建enum时,编译器会为你生成一个相关 ...
- Java编程思想之十四 类型信息
第十四章 类型信息 运行时类型信息使得你可以在程序运行时发现和使用类型信息 14.1 为什么需要RTTI 面向对象编程中基本的目的是:让代码只操作对基类的引用. 多态: import java.uti ...
- Java编程思想学习(十) 正则表达式
正则表达式是一种强大的文本处理工具,使用正则表达式我们可以以编程的方法,构造复杂的文本模式,并且对输入的字符串进行搜索.在我看来,所谓正则表达式就是我们自己定义一些规则,然后就可以验证输入的字符串是不 ...
- Java编程思想学习(十六) 并发编程
线程是进程中一个任务控制流序列,由于进程的创建和销毁需要销毁大量的资源,而多个线程之间可以共享进程数据,因此多线程是并发编程的基础. 多核心CPU可以真正实现多个任务并行执行,单核心CPU程序其实不是 ...
- Java编程思想学习(十五) 注解
注解Annotation又叫元数据,是JDK5中引入的一种以通用格式为程序提供配置信息的方式.使用注解Annotation可以使元数据写在程序源码中,使得代码看起来简洁,同时编译器也提供了对注解Ann ...
- Java编程思想学习(十四) 枚举
关键字enum可以将一组具名的值有限集合创建一种为新的类型,而这些具名的值可以作为常规的程序组件使用. 基本enum特性 调用enum的values()方法可以遍历enum实例,values()方法返 ...
- JAVA编程思想(2) - 操作符(二)
5. 直接常量 -一般来说,假设程序里使用了"直接常量",编译器能够准确的知道要生成什么样的类型.但有时候却是模棱两可的. 这时候须要我们对编译器进行适当的"指导&quo ...
- Java编程思想之十 内部类
可以将一个类定义放在另一个类的定义内部,这就是内部类. 10.1 创建内部类 创建内部类就是把类的定义置于外部类里面. public class Parcell { class contents{ i ...
随机推荐
- SQL IN 一定走索引吗?
摘要 IN 一定走索引吗?那当然了,不走索引还能全部扫描吗?好像之前有看到过什么Exist,IN走不走索引的讨论.但是好像看的太久了,又忘记了.哈哈,如果你也忘记了MySQL中IN是如何查询的,就来复 ...
- jQuery实现C#CheckBoxList模糊搜索
前言 最近开发的一套系统中需要对商品进行管理,在选择商品时,要分别从品牌.型号.商品三个类别分别选择对应的选项才能找到需要的商品,三者的关系为:品牌包含型号,型号包含商品,因此使用了三个不同的 asp ...
- 12、Render函数
1.什么是Render函数 Vue 推荐在绝大多数情况下使用模板来创建你的 HTML.然而在一些场景中,你真的需要 JavaScript 的完全编程的能力.这时你可以用渲染函数,它比模板更接近编译器. ...
- sql比较字符串,比的到底是什么?
sql里有nvarchar类型的日期数据time1:2019-10-09 00:00:00, 现给定string类型日期time2:2019-10-01 23:59:59,比较两个日期的大小, 发现可 ...
- PIE SDK影像快速拼接
1.算法功能简介 快速拼接是对若干幅互为邻接的遥感数字图像拼在一起,构成一幅整体影像的技术过程.PIE支持快速拼接算法功能的执行,下面对快速拼接算法功能进行介绍. 2.算法功能实现说明 2.1 实现步 ...
- 洛谷 p1387最大正方形
洛谷 p1387最大正方形 题目描述 在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长. 输入格式 输入文件第一行为两个整数n,m(1<=n,m<=100),接下来 ...
- MySQL难点语法——子查询
本篇主要通过练习来讲解子查询的知识,进入正题之前,先熟悉数据表,表格的数据可以先不用管,主要是熟悉表格的字段名 这里子查询分为三个部分: 1.where条件子查询 这个子查询和普通的查询没什么区别,主 ...
- ES6 入门系列 (一)ES6的前世今生
要学好javascript , ECMAScript标准比什么都强, ESMAScript标准已经用最严谨的语言和最完美的角度展现了语言的实质和特性. 理解语言的本质后,你已经从沙堆里挑出了珍珠,能经 ...
- Sharding-Jdbc概念与使用技巧
1. Sharding-Jdbc概念与使用技巧 此讲解版本为4.0.0-RC1,目前最新的版本 2019年5月21日发布 1.1. 绑定表 指分片规则一致的主表和子表.例如:t_order表和t_or ...
- uni-app项目配置记录
新建项目 直接使用编辑器快速新建,具体方法很简单,官方文档很详细,这里不在叙说 配置项目: 项目搭建好了之后,我们配置一些 api 和 router,这些直接在插件市场上面进行配置,非常好用 封装的r ...