月度归档:2018年05月

Coursera: Data Visualization week 1

About Coures

We will learn how a computer displays information using computer graphics, and how the human perceives that information visually.
We will also study the forms of data, including quantitative and non-quantitative data, and how they are properly mapped to the elements of a visualization to be perceived well by the observer.

Course Goals and Objectives

Upon successful completion of this course, you will be able to:

  1. Describe how 2-D and 3-D computer graphics are used to visualize data.
  2. Describe how an observer perceives and processes information from a visual display.
  3. Utilize a wide vocabulary of visualization methods and how best to apply them to different kinds of data.
  4. Decide which design styles and colors work best for different visualization situations.
    Visualize data when it is not numerical.
  5. Use techniques for visualizing databases and data mining to help visually sort through massive datasets.
  6. Analyze tasks and build visualization dashboards to provide data to support making a decision.

How to Pass the Course

To qualify for a Course Certificate, simply start verifying your coursework at the beginning of the course, get an 80% or higher on all quizzes and assignments combined, and pay the fee.

Week 1 : The Computer and the Human

Upon successful completion of this module, you will be able to:

  1. Understand what data visualization is and the different kinds of data visualization.
  2. Understand how computer graphics are used to display shapes in 2-D and 3-D, and draw simple 2-D shapes on a web page using Scalable Vector Graphics
  3. Understand how we perceive, learn, and reason about information.

Key Phrases and Concepts

Keep your eyes open for the following key terms or phrases as you complete the readings and interact with the lectures. These topics will help you better understand the content in this module.

  1. Interactive visualization, presentation visualization, and interactive storytelling
  2. Scalable Vector Graphics and the difference between how graphics’ shapes are described versus how they are displayed.
  3. Photorealistic rendering and non-photorealistic rendering
  4. The Model Human Processor and Fitts’s law
  5. Lateral inhibition

设置git走ssr(socks5)代理

  • Kramdown table of contents
    {:toc .toc}

Setting proxy

if socks5(SSR)
first, you need to know you local socks5 proxy ip address and port.
eg. 127.0.0.1 port 1086
then use this command to set proxy

git config --global http.proxy socks5h://127.0.0.1:1080

if http/https
use this command:

git config --global https.proxy http://127.0.0.1:1080git config --global https.proxy https://127.0.0.1:1080

To unset proxys

git config --global --unset http.proxygit config --global --unset https.proxy

IBM的java系列教程笔记

迭代泛型

泛型使用处理一些实体(比如 List)的特殊语法增强了 Java 语言,您通常可能希望逐个元素地处理这些实体。举例而言,如果想迭代 ArrayList,可以将 清单 3 中的代码重写为:

private void processArrayList(ArrayList<String> theList) {
  for (String s : theList) {
    String s = theList.get(aa);
  }
}

此语法适用于任何 Iterable(即实现了 Iterable 接口)的对象类型

一个参数化的 List

在泛型语法中,创建 List 的代码类似于:

List listReference = new concreteListClass();
E(表示元素)是我之前提到的 “东西”。concreteListClass 是 JDK 中您实例化的类。该 JDK 包含多个 List 实现,但您使用了 ArrayList。您可能看到的泛型类的另一种形式是 Class,其中 T 表示类型。在 Java 代码中看到 E 时,它通常指的是某种类型的集合。当您看到 T 时,它表示一个参数化的类。

所以,要创建一个由 java.lang.Integer 组成的 ArrayList,可以这样做:

List<Integer> listOfIntegers = new ArrayList<Integer>();

原文地址
第20单元:泛型

java8中的Lambda表达式和Stream

非常非常棒的JAVA教程,来自于IBM

https://www.ibm.com/developerworks/cn/java/intro-to-java-course/index.html强烈推荐

Syntax

A lambda expression is characterized by the following syntax.
parameter -> expression body
Following are the important characteristics of a lambda expression.

  1. Optional type declaration − No need to declare the type of a parameter. The compiler can inference the same from the value of the parameter.
  2. Optional parenthesis around parameter − No need to declare a single parameter in parenthesis. For multiple parameters, parentheses are required.
  3. Optional curly braces − No need to use curly braces in expression body if the body contains a single statement.
  4. Optional return keyword − The compiler automatically returns the value if the body has a single expression to return the value. Curly braces are required to indicate that expression returns a value.

Lambda Expressions Example

public class Java8Tester {

   public static void main(String args[]) {
      Java8Tester tester = new Java8Tester();

      //with type declaration
      MathOperation addition = (int a, int b) -> a + b;

      //with out type declaration
      MathOperation subtraction = (a, b) -> a - b;

      //with return statement along with curly braces
      MathOperation multiplication = (int a, int b) -> { return a * b; };

      //without return statement and without curly braces
      MathOperation division = (int a, int b) -> a / b;

      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));

      //without parenthesis
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);

      //with parenthesis
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);

      greetService1.sayMessage("Mahesh");
      greetService2.sayMessage("Suresh");
   }

   interface MathOperation {
      int operation(int a, int b);
   }

   interface GreetingService {
      void sayMessage(String message);
   }

   private int operate(int a, int b, MathOperation mathOperation) {
      return mathOperation.operation(a, b);
   }
}

Output:
10 + 5 = 15
10 – 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Mahesh
Hello Suresh

Following are the important points to be considered in the above example.

  1. Lambda expressions are used primarily to define inline implementation of a functional interface, i.e., an interface with a single method only. In the above example, we’ve used various types of lambda expressions to define the operation method of MathOperation interface. Then we have defined the implementation of sayMessage of GreetingService.
  2. Lambda expression eliminates the need of anonymous class and gives a very simple yet powerful functional programming capability to Java.

???

Still got questions about what situations that lambda is required.
need further practice.
simplify interface implementation?


java中的Stream(java8 new feature)

Stream represents a sequence of objects from a source, which supports aggregate operations. Following are the characteristics of a Stream −

  1. Sequence of elements − A stream provides a set of elements of specific type in a sequential manner. A stream gets/computes elements on demand. It never stores the elements.

  2. Source − Stream takes Collections, Arrays, or I/O resources as input source.

  3. Aggregate operations − Stream supports aggregate operations like filter, map, limit, reduce, find, match, and so on.

  4. Pipelining − Most of the stream operations return stream itself so that their result can be pipelined. These operations are called intermediate operations and their function is to take input, process them, and return output to the target. collect() method is a terminal operation which is normally present at the end of the pipelining operation to mark the end of the stream.

  5. Automatic iterations − Stream operations do the iterations internally over the source elements provided, in contrast to Collections where explicit iteration is required.

Generating Streams

With Java 8, Collection interface has two methods to generate a Stream.

  1. stream() − Returns a sequential stream considering collection as its source.

  2. parallelStream() − Returns a parallel Stream considering collection as its source.

为什么需要Stream

Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

更多资料请看Reference中IBM的链接

Reference

Java 8 – Lambda Expressions
Java 8 中的 Streams API 详解 推荐阅读

java中的I/O操作详解和log4j的基本使用

IO流的继承关系图

字节流(Byte Streams)

字节流处理原始的二进制数据 I/O。输入输出的是8位字节,相关的类为 InputStream 和 OutputStream.

字节流的类有许多。为了演示字节流的工作,我们将重点放在文件 I/O 字节流 FileInputStream 和 FileOutputStream 上。其他种类的字节流用法类似,主要区别在于它们构造的方式,大家可以举一反三。

用法
下面一例子 CopyBytes, 从 xanadu.txt 文件复制到 outagain.txt,每次只复制一个字节:

public class CopyBytes {
	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		FileInputStream in = null;
		FileOutputStream out = null;

		try {
			in = new FileInputStream("resources/xanadu.txt");
			out = new FileOutputStream("resources/outagain.txt");
			int c;

			while ((c = in.read()) != -1) {
				out.write(c);
			}
		} finally {
			if (in != null) {
				in.close();
			}
			if (out != null) {
				out.close();
			}
		}
	}
}

CopyBytes 花费其大部分时间在简单的循环里面,从输入流每次读取一个字节到输出流,如图所示:

记得始终关闭流

不再需要一个流记得要关闭它,这点很重要。所以,CopyBytes 使用 finally 块来保证即使发生错误两个流还是能被关闭。这种做法有助于避免严重的资源泄漏。

一个可能的错误是,CopyBytes 无法打开一个或两个文件。当发生这种情况,对应解决方案是判断该文件的流是否是其初始 null 值。这就是为什么 CopyBytes 可以确保每个流变量在调用前都包含了一个对象的引用。

何时不使用字节流

CopyBytes 似乎是一个正常的程序,但它实际上代表了一种低级别的 I/O,你应该避免。因为 xanadu.txt 包含字符数据时,最好的方法是使用字符流,下文会有讨论。字节流应只用于最原始的 I/O。所有其他流类型是建立在字节流之上的。

字符流(Character Streams)

字符流处理字符数据的 I/O,自动处理与本地字符集转化。

Java 平台存储字符值使用 Unicode 约定。字符流 I/O 会自动将这个内部格式与本地字符集进行转换。在西方的语言环境中,本地字符集通常是 ASCII 的8位超集。

对于大多数应用,字符流的 I/O 不会比 字节流 I/O操作复杂。输入和输出流的类与本地字符集进行自动转换。使用字符的程序来代替字节流可以自动适应本地字符集,并可以准备国际化,而这完全不需要程序员额外的工作。

如果国际化不是一个优先事项,你可以简单地使用字符流类,而不必太注意字符集问题。以后,如果国际化成为当务之急,你的程序可以方便适应这种需求的扩展。见国际化获取更多信息。

Java采用 Unicode 字符集,每个字符和汉字都采用2个字节进行编码,ASCII 码是 Unicode 编码的自集
InputStreamReader 是 字节流 到 字符桥的桥梁 ( byte->char 读取字节然后用特定字符集编码成字符)
OutputStreamWriter是 字符流 到 字节流的桥梁 ( char->byte )
他们是在字节流的基础上加了桥梁作用,所以构造他们时要先构造普通文件流
我们常用的是:
BufferedReader 方法:readLine()
PrintWriter 方法:println()

用法

字符流类描述在 Reader 和 Writer。而对应文件 I/O ,在 FileReader 和 FileWriter,下面是一个 CopyCharacters 例子:

public class CopyCharacters {
	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
        FileReader inputStream = null;
        FileWriter outputStream = null;

        try {
            inputStream = new FileReader("resources/xanadu.txt");
            outputStream = new FileWriter("resources/characteroutput.txt");

            int c;
            while ((c = inputStream.read()) != -1) {
                outputStream.write(c);
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
	}
}

CopyCharacters 与 CopyBytes 是非常相似的。最重要的区别在于 CopyCharacters 使用的 FileReader 和 FileWriter 用于输入输出,而 CopyBytes 使用 FileInputStream 和FileOutputStream 中的。请注意,这两个CopyBytes和CopyCharacters使用int变量来读取和写入;在 CopyCharacters,int 变量保存在其最后的16位字符值;在 CopyBytes,int 变量保存在其最后的8位字节的值。

字符使用字节流

字符流往往是对字节流的“包装”。字符流使用字节流来执行物理I/O,同时字符流处理字符和字节之间的转换。例如,FileReader 使用 FileInputStream,而 FileWriter使用的是 FileOutputStream。

面向行的I/O操作

字符 I/O 通常发生在较大的单位不是单个字符。一个常用的单位是行:用行结束符结尾。行结束符可以是回车/换行序列(“\r\n”),一个回车(“\r”),或一个换行符(“\n”)。支持所有可能的行结束符,程序可以读取任何广泛使用的操作系统创建的文本文件。

修改 CopyCharacters 来演示如使用面向行的 I/O。要做到这一点,我们必须使用两个类,BufferedReader 和 PrintWriter 的。

该 CopyLines 示例调用 BufferedReader.readLine 和 PrintWriter.println 同时做一行的输入和输出。

public class CopyLines {
	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		    BufferedReader inputStream = null;
        PrintWriter outputStream = null;

        try {
            inputStream = new BufferedReader(new FileReader("resources/xanadu.txt"));
            outputStream = new PrintWriter(new FileWriter("resources/characteroutput.txt"));

            String l;
            while ((l = inputStream.readLine()) != null) {
                outputStream.println(l);
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
	}
}

调用 readLine 按行返回文本行。CopyLines 使用 println 输出带有当前操作系统的行终止符的每一行。这可能与输入文件中不是使用相同的行终止符。

缓冲流(Buffered Streams)

缓冲流通过减少调用本地 API 的次数来优化的输入和输出。

目前为止,大多数时候我们到看到使用非缓冲 I/O 的例子。这意味着每次读或写请求是由基础 OS 直接处理。这可以使一个程序效率低得多,因为每个这样的请求通常引发磁盘访问,网络活动,或一些其它的操作,而这些是相对昂贵的。

为了减少这种开销,所以 Java 平台实现缓冲 I/O 流。缓冲输入流从被称为缓冲区(buffer)的存储器区域读出数据;仅当缓冲区是空时,本地输入 API 才被调用。同样,缓冲输出流,将数据写入到缓存区,只有当缓冲区已满才调用本机输出 API。

程序可以转换的非缓冲流为缓冲流,这里用非缓冲流对象传递给缓冲流类的构造器。

inputStream = new BufferedReader(new FileReader("xanadu.txt"));
outputStream = new BufferedWriter(new FileWriter("characteroutput.txt"));

用于包装非缓存流的缓冲流类有4个:BufferedInputStream 和 BufferedOutputStream 用于创建字节缓冲字节流, BufferedReader 和 BufferedWriter 用于创建字符缓冲字节流。

刷新缓冲流

刷新缓冲区是指在某个缓冲的关键点就可以将缓冲输出,而不必等待它填满。

一些缓冲输出类通过一个可选的构造函数参数支持 autoflush(自动刷新)。当自动刷新开启,某些关键事件会导致缓冲区被刷新。例如,自动刷新 PrintWriter 对象在每次调用 println 或者 format 时刷新缓冲区。查看 Formatting 了解更多关于这些的方法。

如果要手动刷新流,请调用其 flush 方法。flush 方法可以用于任何输出流,但对非缓冲流是没有效果的。

扫描(Scanning)和格式化(Formatting)

扫描

将其输入分解为标记
默认情况下,Scanner 使用空格字符分隔标记。(空格字符包括空格,制表符和行终止符。为完整列表,请参阅 Character.isWhitespace)。示例 ScanXan 读取 xanadu.txt 的单个词语并打印他们:

public class ScanXan {
	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		Scanner s = null;

        try {
            s = new Scanner(new BufferedReader(new FileReader("resources/xanadu.txt")));

            while (s.hasNext()) {
                System.out.println(s.next());
            }
        } finally {
            if (s != null) {
                s.close();
            }
        }
	}
}

虽然 Scanner 不是流,但你仍然需要关闭它,以表明你与它的底层流执行完成。

调用 useDelimiter() ,指定一个正则表达式可以使用不同的标记分隔符。例如,假设您想要标记分隔符是一个逗号,后面可以跟空格。你会调用s.useDelimiter(",\\s*");

转换成独立标记
该 ScanXan 示例是将所有的输入标记为简单的字符串值。Scanner 还支持所有的 Java 语言的基本类型(除 char),以及 BigInteger 和 BigDecimal 的。此外,数字值可以使用千位分隔符。因此,在一个美国的区域设置,Scanner 能正确地读出字符串“32,767”作为一个整数值。

这里要注意的是语言环境,因为千位分隔符和小数点符号是特定于语言环境。所以,下面的例子将无法正常在所有的语言环境中,如果我们没有指定 scanner 应该用在美国地区工作。可能你平时并不用关心,因为你输入的数据通常来自使用相同的语言环境。可以使用下面的语句来设置语言环境:s.useLocale(Locale.US);
该 ScanSum 示例是将读取的 double 值列表进行相加:

public class ScanSum {
	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		Scanner s = null;
		double sum = 0;

		try {
			s = new Scanner(new BufferedReader(new FileReader("resources/usnumbers.txt")));
			s.useLocale(Locale.US);

			while (s.hasNext()) {
				if (s.hasNextDouble()) {
					sum += s.nextDouble();
				} else {
					s.next();
				}
			}
		} finally {
			s.close();
		}

		System.out.println(sum);
	}
}

输出为:1032778.74159

格式化

实现格式化流对象要么是 字符流类的 PrintWriter 的实例,或为字节流类的 PrintStream 的实例。

注:对于 PrintStream 对象,你很可能只需要 System.out 和 System.err。 (请参阅命令行I/O)当你需要创建一个格式化的输出流,请实例化 PrintWriter,而不是 PrintStream。

像所有的字节和字符流对象一样,PrintStream 和 PrintWriter 的实例实现了一套标准的 write 方法用于简单的字节和字符输出。此外,PrintStream 和 PrintWriter 的执行同一套方法,将内部数据转换成格式化输出。提供了两个级别的格式:

  1. print 和 println 在一个标准的方式里面格式化独立的值 。
  2. format 用于格式化几乎任何数量的格式字符串值,且具有多种精确选择。

print 和 println 方法*
调用 print 或 println 输出使用适当 toString 方法变换后的值的单一值。我们可以看到这 Root 例子:

public class Root {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		    int i = 2;
        double r = Math.sqrt(i);

        System.out.print("The square root of ");
        System.out.print(i);
        System.out.print(" is ");
        System.out.print(r);
        System.out.println(".");

        i = 5;
        r = Math.sqrt(i);
        System.out.println("The square root of " + i + " is " + r + ".");
	}
}

输出为:
The square root of 2 is 1.4142135623730951.
The square root of 5 is 2.23606797749979.

format 方法
该 format 方法用于格式化基于 format string(格式字符串) 多参。格式字符串包含嵌入了 format specifiers (格式说明)的静态文本;除非使用了格式说明,否则格式字符串输出不变。

格式字符串支持许多功能。在本教程中,我们只介绍一些基础知识。有关完整说明,请参阅 API 规范关于格式字符串语法。

Root2 示例在一个 format 调用里面设置两个值:

public class Root2 {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		int i = 2;
        double r = Math.sqrt(i);

        System.out.format("The square root of %d is %f.%n", i, r);
	}
}

输出为:The square root of 2 is 1.414214.
像本例中所使用的格式为:

  1. d 格式化整数值为小数
  2. f 格式化浮点值作为小数
  3. n 输出特定于平台的行终止符。

这里有一些其他的转换格式:

  1. x 格式化整数为十六进制值
  2. s 格式化任何值作为字符串
  3. tB 格式化整数作为一个语言环境特定的月份名称。

还有许多其他的转换。
注意:除了 %% 和 %n,其他格式符都要匹配参数,否则抛出异常。在 Java 编程语言中,\ n转义总是产生换行符(\u000A)。不要使用\ñ除非你特别想要一个换行符。为了针对本地平台得到正确的行分隔符,请使用%n。

除了用于转换,格式说明符可以包含若干附加的元素,进一步定制格式化输出。下面是一个 Format 例子,使用一切可能的一种元素。

public class Format {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		 System.out.format("%f, %1$+020.10f %n", Math.PI);
	}
}

输出为:3.141593, +00000003.1415926536
附加元素都是可选的。下图显示了长格式符是如何分解成元素

元件必须出现在显示的顺序。从合适的工作,可选的元素是:(摘抄博主原文,可能是用翻译直接翻过来的,不通顺)

  1. Precision(精确)。对于浮点值,这是格式化值的数学精度。对于 s 和其他一般的转换,这是格式化值的最大宽度;该值右截断,如果有必要的。
  2. Width(宽度)。格式化值的最小宽度;如有必要,该值被填充。默认值是左用空格填充。
  3. Flags(标志)指定附加格式设置选项。在 Format 示例中,+ 标志指定的数量应始终标志格式,以及0标志指定0是填充字符。其他的标志包括 – (垫右侧)和(与区域特定的千位分隔符格式号)。请注意,某些标志不能与某些其他标志或与某些转换使用。

上面的格式和C语言printf的使用方法是相类似的,不必过于细看
4. Argument Index(参数索引)允许您指定的参数明确匹配。您还可以指定<到相同的参数作为前面的说明一致。这样的例子可以说:System.out.format(“%F,%<+ 020.10f%N”,Math.PI);
Argument Index:可以理解为自由匹配参数,不用按照”,”后面所写的顺序了.例如:

import java.util.Formatter;
//  j  a  va  2  s. c om
public class Main {
  public static void main(String args[]) {
    Formatter fmt = new Formatter();
    fmt.format("%3$d %1$d %2$d", 10, 20, 30);
    System.out.println(fmt);
  }
}

输出:
30 10 20
%3$d表示用后面的第三个参数(30),匹配%d的格式来输出,同理,1$就是表示匹配第一个参数了,所以
最后的输出结果不是按照所传入的顺序,而是按照$所指定的顺序.

命令行 I/O

命令行 I/O 描述了标准流(Standard Streams)和控制台(Console)对象。

Java 支持两种交互方式:标准流(Standard Streams)和通过控制台(Console)。

标准流

标准流是许多操作系统的一项功能。默认情况下,他们从键盘读取输入和写出到显示器。它们还支持对文件和程序之间的 I/O,但该功能是通过命令行解释器,而不是由程序控制。

Java平台支持三种标准流:标准输入(Standard Input, 通过 System.in 访问)、标准输出(Standard Output, 通过System.out 的访问)和标准错误( Standard Error, 通过System.err的访问)。这些对象被自动定义,并不需要被打开。标准输出和标准错误都用于输出;错误输出允许用户转移经常性的输出到一个文件中,仍然能够读取错误消息。

您可能希望标准流是字符流,但是,由于历史的原因,他们是字节流。 System.out 和System.err 定义为 PrintStream 的对象。虽然这在技术上是一个字节流,PrintStream 利用内部字符流对象来模拟多种字符流的功能。

相比之下,System.in 是一个没有字符流功能的字节流。若要想将标准的输入作为字符流,可以包装 System.in 在 InputStreamReader

InputStreamReader cin = new InputStreamReader(System.in);

Console (控制台)

更先进的替代标准流的是 Console 。这个单一,预定义的 Console 类型的对象,有大部分的标准流提供的功能,另外还有其他功能。Console 对于安全的密码输入特别有用。Console 对象还提供了真正的输入输出字符流,是通过 reader 和 writer 方法实现的。

若程序想使用 Console ,它必须尝试通过调用 System.console() 检索 Console 对象。如果 Console 对象存在,通过此方法将其返回。如果返回 NULL,则 Console 操作是不允许的,要么是因为操作系统不支持他们或者是因为程序本身是在非交互环境中启动的。

Console 对象支持通过读取密码的方法安全输入密码。该方法有助于在两个方面的安全。第一,它抑制回应,因此密码在用户的屏幕是不可见的。第二,readPassword 返回一个字符数组,而不是字符串,所以,密码可以被覆盖,只要它是不再需要就可以从存储器中删除。

Password 例子是一个展示了更改用户的密码原型程序。它演示了几种 Console 方法

public class Password {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Console c = System.console();
        if (c == null) {
            System.err.println("No console.");
            System.exit(1);
        }

        String login = c.readLine("Enter your login: ");
        char [] oldPassword = c.readPassword("Enter your old password: ");

        if (verify(login, oldPassword)) {
            boolean noMatch;
            do {
                char [] newPassword1 = c.readPassword("Enter your new password: ");
                char [] newPassword2 = c.readPassword("Enter new password again: ");
                noMatch = ! Arrays.equals(newPassword1, newPassword2);
                if (noMatch) {
                    c.format("Passwords don't match. Try again.%n");
                } else {
                    change(login, newPassword1);
                    c.format("Password for %s changed.%n", login);
                }
                Arrays.fill(newPassword1, ' ');
                Arrays.fill(newPassword2, ' ');
            } while (noMatch);
        }

        Arrays.fill(oldPassword, ' ');
    }

    // Dummy change method.
    static boolean verify(String login, char[] password) {
        // This method always returns
        // true in this example.
        // Modify this method to verify
        // password according to your rules.
        return true;
    }

    // Dummy change method.
    static void change(String login, char[] password) {
        // Modify this method to change
        // password according to your rules.
    }
}

上面的流程是:

  1. 尝试检索 Console 对象。如果对象是不可用,中止。
  2. 调用 Console.readLine 提示并读取用户的登录名。
  3. 调用 Console.readPassword 提示并读取用户的现有密码。
  4. 调用 verify 确认该用户被授权可以改变密码。(在本例中,假设 verify 是总是返回true )
  5. 重复下列步骤,直到用户输入的密码相同两次:
     调用 Console.readPassword 两次提示和读一个新的密码。 如果用户输入的密码两次,调用 change 去改变它。 (同样,change 是一个虚拟的方法) 用空格覆盖这两个密码。
  6. 用空格覆盖旧的密码。

数据流(Data Streams), 对象流(Object Streams)由于篇幅原因,请参考

原文链接
Java 编程要点| 非常棒的开源java书籍
gitbooks链接

Log4j基本用法

篇幅原因,请点这里

参考

Java 编程要点之 I/O 流详解
Java I/O 操作及优化详细介绍
《JAVA I/O最详解》
Formatter Argument Index
Java String Format Examples

java中的I/O操作详解和log4j的基本使用 – 2(Log4j)

Features

  1. It is thread-safe.
  2. It is optimized for speed.
  3. It is based on a named logger hierarchy.
  4. It supports multiple output appenders per logger.
  5. It supports internationalization.
  6. It is not restricted to a predefined set of facilities.
  7. Logging behavior can be set at runtime using a configuration file.
  8. It is designed to handle Java Exceptions from the start.
  9. It uses multiple levels, namely ALL, TRACE, DEBUG, INFO, WARN, ERROR and FATAL.
  10. The format of the log output can be easily changed by extending the Layout class.
  11. The target of the log output as well as the writing strategy can be altered by implementations of the Appender interface.
  12. It is fail-stop. However, although it certainly strives to ensure delivery, log4j does not guarantee that each log statement will be delivered to its destination.

Architecture


Applications using the Log4j 2 API will request a Logger with a specific name from the LogManager. The LogManager will locate the appropriate LoggerContext and then obtain the Logger from it. If the Logger must be created it will be associated with the LoggerConfig that contains either a) the same name as the Logger, b) the name of a parent package, or c) the root LoggerConfig. LoggerConfig objects are created from Logger declarations in the configuration. The LoggerConfig is associated with the Appenders that actually deliver the LogEvents.

Configuration

The log4j.properties file is a log4j configuration file that keeps properties in key-value pairs. By default, the LogManager looks for a file named log4j.properties in the CLASSPATH.

Simple Usage

Hello World

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class HelloWorld {
    private static final Logger logger = LogManager.getLogger("HelloWorld");
    public static void main(String[] args) {
        logger.info("Hello, World!");
    }
}

log4j.properties Syntax:

# Define the root logger with appender X
log4j.rootLogger = DEBUG, X

# Set the appender named X to be a File appender
log4j.appender.X=org.apache.log4j.FileAppender

# Define the layout for X appender
log4j.appender.X.layout=org.apache.log4j.PatternLayout
log4j.appender.X.layout.conversionPattern=%m%n

log4j.properties Example
Using the above syntax, we define the following in log4j.properties file:

  1. The level of the root logger is defined as DEBUG, The DEBUG appender named FILE to it.

  2. The appender FILE is defined as org.apache.log4j.FileAppender. It writes to a file named log.out located in the log directory.

  3. The layout pattern defined is %m%n, which means the printed logging message will be followed by a newline character.

    # Define the root logger with appender filelog4j.rootLogger = DEBUG, FILE# Define the file appenderlog4j.appender.FILE=org.apache.log4j.FileAppenderlog4j.appender.FILE.File=
     
    #123;log}/log.out# Define the layout for file appenderlog4j.appender.FILE.layout=org.apache.log4j.PatternLayoutlog4j.appender.FILE.layout.conversionPattern=%m%n

More info

Log4j can save logfile into a file or DB or just print it out.
For later reference, you can check this Quick Guide

Reference

log4j Tutorial | pretty useful website for self-learner
Apache Log4j 2 Offical Site

数字,字符串和日期类的基本操作

基本类型的封装类

所有的基本类型,都有对应的类类型,比如int对应的类是Integer,这种类就叫做封装类.

基本类型和封装类的转化

public class TestNumber {

    public static void main(String[] args) {
        int i = 5;

        //基本类型转换成封装类型
        Integer it = new Integer(i);
        //封装类型转换成基本类型
        int i2 = it.intValue();
        //自动转换就叫装箱
        Integer it2 = i;
        //自动转换就叫拆箱
        int i3 = it;
    }
}

TODO: StringBuffer & StringBuilder
Java基础之String、StringBuffer与StringBuilder的区别及应用场景

日期和时间

除了String这个类在日常的项目中比较常用之外,有关时间和日期的操作也是经常遇到的,本篇就讲详细介绍下Java API中对时间和日期的支持。其实在Java 8之前时间日期的API并不是很好用,以至于人们在项目中大多使用的是一个第三方库 Joda-Time,当然Java 8 吸收了该库的大部分优点,改进了相关API,现在的时间日期处理接口相对以前来说是好用很多,本篇也将学习下这个优秀的第三方库。下面是本篇主要涉及内容:

  1. 古老的Date类
  2. 处理年月日的年历类Calendar
  3. 格式化字符串和日期对象的DateFormat格式转换类
  4. 好用的SimpleDateFormat实现类
  5. Joda-Time库

java.util.Date类
类 Date 表示特定的瞬间,精确到毫秒。从 JDK 1.1 开始,应该使用 Calendar 类实现日期和时间字段之间转换,使用 DateFormat 类来格式化和分析日期字符串。Date 中的把日期解释为年、月、日、小时、分钟和秒值的方法已废弃。
提供了很多的方法,但是很多已经过时,不推荐使用,下面仅仅列出没有过时的方法:

构造方法摘要

Date()
分配 Date 对象并用当前时间初始化此对象,以表示分配它的时间(精确到毫秒)。
Date(long date)
分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。

构造方法源码:

public Date(long date) {
        fastTime = date;
    }
public Date() {
        this(System.currentTimeMillis());
    }

方法摘要

boolean after(Date when)
测试此日期是否在指定日期之后。

boolean before(Date when)
测试此日期是否在指定日期之前。

Object clone()
返回此对象的副本。

int compareTo(Date anotherDate)
比较两个日期的顺序。

boolean equals(Object obj)
比较两个日期的相等性。

long getTime()
返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。

int hashCode()
返回此对象的哈希码值。

void setTime(long time)
设置此 Date 对象,以表示 1970 年 1 月 1 日 00:00:00 GMT 以后 time 毫秒的时间点。

String toString()
把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中:
dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。
mon 是月份 (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)。
dd 是一月中的某一天(01 至 31),显示为两位十进制数。
hh 是一天中的小时(00 至 23),显示为两位十进制数。
mm 是小时中的分钟(00 至 59),显示为两位十进制数。
ss 是分钟中的秒数(00 至 61),显示为两位十进制数。
zzz 是时区(并可以反映夏令时)。标准时区缩写包括方法 parse 识别的时区缩写。如果不提供时区信息,则 zzz 为空,即根本不包括任何字符。
yyyy 是年份,显示为 4 位十进制数。

例子:

import java.util.Date;

/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-30
* Time: 8:45:44
* 日期测试
*/
public class TestDate {
    public static void main(String args[]) {
        TestDate nowDate = new TestDate();
        nowDate.getSystemCurrentTime();
        nowDate.getCurrentDate();
    }

    /**
     * 获取系统当前时间
     * System.currentTimeMillis()返回系统当前时间,结果为1970年1月1日0时0分0秒开始,到程序执行取得系统时间为止所经过的毫秒数
     * 1秒=1000毫秒
     */
    public void getSystemCurrentTime() {
        System.out.println("----获取系统当前时间----");
        System.out.println("系统当前时间 = " + System.currentTimeMillis());
    }

    /**
     * 通过Date类获取当前日期和当前时间
     * date.toString()把日期转换为dow mon dd hh:mm:ss zzz yyyy
     */
    public void getCurrentDate() {
        System.out.println("----获取系统当前日期----");
        //创建并初始化一个日期(初始值为当前日期)
        Date date = new Date();
        System.out.println("现在的日期是 = " + date.toString());
        System.out.println("自1970年1月1日0时0分0秒开始至今所经历的毫秒数 = " + date.getTime());
    }
}

返回结果:

运行结果:
—-获取系统当前时间—-
系统当前时间 = 1196413077278
—-获取系统当前日期—-
现在的日期是 = Fri Nov 30 16:57:57 CST 2007
自1970年1月1日0时0分0秒开始至今所经历的毫秒数 = 1196413077278

Process finished with exit code 0

java.text.DateFormat抽象类的使用
DateFormat是一个抽象类,该类主要用于实现Date对象和字符串之间相互转换, 涉及到两个转换的方法:

//将Date类型转换为String类型
public final String format(Date date)
//将String类型转换Date类型
public Date parse(String source)

除此之外,DateFormat还提供了四个静态常量,代表着四种不同的风格。不同的风格输出信息的内容详尽程度不同,默认的风格是MEDIUM。

public static final int FULL = 0;
public static final int LONG = 1;

public static final int MEDIUM = 2;

public static final int SHORT = 3;

public static final int DEFAULT = MEDIUM;

java.text.SimpleDateFormat(DateFormat的直接子类)的使用
SimpleDateFormat是DateFormat的一个优秀的实现类,它增强了一个重要的性质。它允许自定义格式输出模板。构造SimpleDateFormat实例的时候,可以传入一个pattern作为输出模板。看个例子:

public static void main(String[] args) {
    Calendar c = Calendar.getInstance();
    SimpleDateFormat sm = new SimpleDateFormat("yyyy年MM月dd日 E HH时mm分ss秒");
    System.out.println(sm.format(c.getTime()));
}

输出结果:
2017年05月29日 星期一 20时25分31秒
上述的代码中,字符串yyyy年MM月dd日 E HH时mm分ss秒就是一个模板pattern,其中:


更多的参考信息可以查看JDK API文档,下面给出一个综合实例:

import java.util.Date;
import java.util.Locale;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-30
* Time: 11:20:58
* To change this template use File | Settings | File Templates.
*/
public class TestSimpleDateFormat {
    public static void main(String args[]) throws ParseException {
        TestSimpleDateFormat test = new TestSimpleDateFormat();
        test.testDateFormat();

    }

    public void testDateFormat() throws ParseException {
        //创建日期
        Date date = new Date();

        //创建不同的日期格式
        DateFormat df1 = DateFormat.getInstance();
        DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss EE");
        DateFormat df3 = DateFormat.getDateInstance(DateFormat.FULL, Locale.CHINA);     //产生一个指定国家指定长度的日期格式,长度不同,显示的日期完整性也不同
        DateFormat df4 = new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒 EE", Locale.CHINA);
        DateFormat df5 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss EEEEEE", Locale.US);
        DateFormat df6 = new SimpleDateFormat("yyyy-MM-dd");
        DateFormat df7 = new SimpleDateFormat("yyyy年MM月dd日");

        //将日期按照不同格式进行输出
        System.out.println("-------将日期按照不同格式进行输出------");
        System.out.println("按照Java默认的日期格式,默认的区域                      : " + df1.format(date));
        System.out.println("按照指定格式 yyyy-MM-dd hh:mm:ss EE ,系统默认区域      :" + df2.format(date));
        System.out.println("按照日期的FULL模式,区域设置为中文                      : " + df3.format(date));
        System.out.println("按照指定格式 yyyy年MM月dd日 hh时mm分ss秒 EE ,区域为中文 : " + df4.format(date));
        System.out.println("按照指定格式 yyyy-MM-dd hh:mm:ss EE ,区域为美国        : " + df5.format(date));
        System.out.println("按照指定格式 yyyy-MM-dd ,系统默认区域                  : " + df6.format(date));

        //将符合该格式的字符串转换为日期,若格式不相配,则会出错
        Date date1 = df1.parse("07-11-30 下午2:32");
        Date date2 = df2.parse("2007-11-30 02:51:07 星期五");
        Date date3 = df3.parse("2007年11月30日 星期五");
        Date date4 = df4.parse("2007年11月30日 02时51分18秒 星期五");
        Date date5 = df5.parse("2007-11-30 02:51:18 Friday");
        Date date6 = df6.parse("2007-11-30");

        System.out.println("-------输出将字符串转换为日期的结果------");
        System.out.println(date1);
        System.out.println(date2);
        System.out.println(date3);
        System.out.println(date4);
        System.out.println(date5);
        System.out.println(date6);
    }
}

运行结果:
——-将日期按照不同格式进行输出——
按照Java默认的日期格式,默认的区域 : 07-11-30 下午5:04
按照指定格式 yyyy-MM-dd hh:mm:ss EE ,系统默认区域 :2007-11-30 05:04:10 星期五
按照日期的FULL模式,区域设置为中文 : 2007年11月30日 星期五
按照指定格式 yyyy年MM月dd日 hh时mm分ss秒 EE ,区域为中文 : 2007年11月30日 05时04分10秒 星期五
按照指定格式 yyyy-MM-dd hh:mm:ss EE ,区域为美国 : 2007-11-30 05:04:10 Friday
按照指定格式 yyyy-MM-dd ,系统默认区域 : 2007-11-30
——-输出将字符串转换为日期的结果——
Fri Nov 30 14:32:00 CST 2007
Fri Nov 30 02:51:07 CST 2007
Fri Nov 30 00:00:00 CST 2007
Fri Nov 30 02:51:18 CST 2007
Fri Nov 30 02:51:18 CST 2007
Fri Nov 30 00:00:00 CST 2007

Process finished with exit code 0

java.util.Calendar(抽象类)
以前我们是可以使用Date来处理日期年月日的,但是由于该类不支持国际化等原因,现在其中大部分方法被注解,不再推荐使用,现在的Date类更像是代表着某一个时刻的对象,而处理年月日的这种转换则完全交给了Calendar类处理。所以Calendar目前是日期时间处理中的核心类,接下来我们看看其中源码:

//和Date一样封装了毫秒属性
protected long  time;
protected int           fields[];
//封装了十七个静态常量
public final static int ERA = 0;
public final static int YEAR = 1;
public final static int MONTH = 2;
public final static int WEEK_OF_YEAR = 3;
.........
public final static int DST_OFFSET = 16;

在Calendar的内部封装了17个静态常量,这些常量将会作为索引用来检索fields属性,例如:fields[YEAR]将返回当前毫秒值对应的日期时间的年份部分,fields[MONTH]将返回的是月份部分的值等等。至于这些值是哪里来的,等我们介绍到后续源码的时候再说明,此处只需要理解这些常量的作用即可。

该类是抽象类,我们使用工厂方法获取该类实例:

public static Calendar getInstance()
{
    return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}

public static Calendar getInstance(TimeZone zone)
{
    return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}

public static Calendar getInstance(Locale aLocale)
{
    return createCalendar(TimeZone.getDefault(), aLocale);
}

public static Calendar getInstance(TimeZone zone,
                                       Locale aLocale)
{
    return createCalendar(zone, aLocale);
}

主要有四个方法用于创建Calendar实例,其实内部调用的是同一的方法只是传入的参数的值不同。创建一个Calend 实例需要两个参数,一个是TimeZone时区,另一个是Locale语言国家。因为每个国家或地区他们表示时间的形式是不一样的,所以我们需要通过这两个参数确定具体需要使用的格式,当然是以本地时间作为fastTime的值的,如果我们没有指定时区和国家语言,那么将会默认使用本机系统信息。接下来我们看如何通过获取到Calendar实例完成对日期时间进行计算。
我们有获取和设置内部代表毫秒的time属性:

public final Date getTime() {
    return new Date(getTimeInMillis());
}
public void setTimeInMillis(long millis){

}

也有获取上述介绍的17中属性的方法:

public int get(int field)
{
    complete();
    return internalGet(field);
}

其中complete方法就是调用了本地函数完成对fields属性中没有值的元素赋值。 调用internalGet方法其实就是调用的fields[field],为我们返回指定属性的结果值。我们可以看个例子:

public static void main(String[] args){
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.get(Calendar.YEAR));
    System.out.println(calendar.get(Calendar.MONTH));
    System.out.println(calendar.get(Calendar.AM_PM))
}

结果如下:


上述代码运行在不同的时候的结果都是不一样的,写作时的时间:2017/5/29 14:02。需要注意一点的是,month属性是从0开始的,也就是0表示一月,4表示5月,星期也是一样。此外,上述中的AM_PM表示的是上下午的概念,上午为0,下午为1。

开源第三方库Joda-Time
Joda-Time库中的内容还是很多的,我们简单了解下基本的使用即可,至于深入学习该库,大家可以自行尝试,此处限于篇幅,不再赘述。在该库中DateTime相当于jdk中Calendar,主要完成对日期年月日的计算操作。首先我们通过简单易理解的方式创建DateTime的实例对象:

//2017-05-29 21:40
DateTime dt = new DateTime(2017,5,29,21,40);

//2017-05-29 21:40 50秒
DateTime dt2 = new DateTime(2017,5,29,21,40,50);

创建一个日期实例比Calendar中为每个属性set值方便多了。在该库中获取日期的操作被分解了,不像Calendar中共享一个int数组。

DateTime dt = new DateTime(2017,5,29,21,40);
System.out.println("year: "+dt.getYear());
System.out.println("month: "+dt.getMonthOfYear());
System.out.println("day: "+dt.getDayOfMonth());
System.out.println("hour: "+dt.getHourOfDay());
System.out.println("minute: "+dt.getMinuteOfHour());
System.out.println("second: "+dt.getSecondOfMinute());
System.out.println("millisecond: " +dt.getMillisOfSecond());
System.out.println("day_of_week: " +dt.getDayOfWeek());

我们也可以直接使用DateTime的tostring方法来实现将日期转换成指定pattern的字符串,例如:

DateTime dt = new DateTime(2017,5,29,21,40);
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss"));

上述代码将会把日期类型按照指定的模板输出,该Joda-Time库中内容很多,此处就简单介绍到这, 感兴趣的同学可以自行研究,该库的核心优势就在于它将很多复杂的操作分解为单个简单操作,这也是我们程序设计中核心的思维方式。

参考

JAVA的日期类DATE
Java中的日期操作
深入理解Java常用类—–时间日期
关于 Java 的静态工厂方法,看这一篇就够了!

java异常研究,详解

概述

在Java中,所有的事件都能由类描述,Java中的异常就是由java.lang包下的异常类描述的。

1、Throwable(可抛出): 异常类的最终父类,它有两个子类,Error与Exception。
Throwable中常用方法有:
getCause():返回抛出异常的原因。如果 cause 不存在或未知,则返回 null。
getMeage():返回异常的消息信息。
printStackTrace():对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的值。

2、Error(错误): 表示程序无法处理的错误,一般与程序员的执行操作无关。理论上这些错误是不允许发生的,如果发生,也不应该试图通过程序去处理,所以Error不是try-catch的处理对象,而JVM一般的处理方式是终止发生错误的线程。Error类常见子类有VirtualMachineError与AWTError。

3、VirtualMachineError(虚拟机错误): 表示虚拟机出现错误。
在Java运行时内存中,除程序计数器外的虚拟机栈、堆、方法区在请求的内存无法被满足时都会抛出OutOfMemoryError;
而如果线程请求的栈深度超出虚拟机允许的深度时,就会抛出StackOverFlowError。

4、AWTError(AWT组件出错): 这个错误并不是很常用。但是提一下AWT与Swing的区别,AWT是使用操作系统中的图形函数的抽象窗口工具,用C\C++编写,为了实现Java“一次编译,处处运行”的理念,AWT使用各个操作系统图形函数的交集,所以功能较差,但运行速度较快,适合嵌入式Java;
而Swing组件是基于AWT编写的图形界面系统,它用纯Java编写,所以必然实现了“一次编译,处处运行”,但相较于AWT运行速度较慢,适合PC使用。

5、Exception(异常): 出现原因取决于程序,所以程序也理应通过try-catch处理。
异常分为两类:可查异常与不可查异常。

可查异常:编译器要求必须处理,否则不能通过编译,使用try-catch捕获或者throws抛出。常见的可查异常有IOException(IO错误)及其子类EOFExcption(文件已结束异常)、FileNotFound(文件未找到异常)。

不可查异常(也叫运行时异常):编译期不会检查,所以在程序中可不处理,但如果发生,会在运行时抛出。所以这类异常要尽量避免!常见的不可查异常都是RuntimeException类及其子类。

1’ NullPointerException:空指针异常。 调用了不存在的对象或未经实例化或初始化的对象时会抛出,如当试图操作一个空对象(赋值为null)的属性、方法时就会抛出。

(实例化:通俗的理解就是为对象开辟空间,使其可在规定范围内被调用。注意:User u;这只是一个对象声明,并没有进行实例化或初始化。
初始化:就是把实例化后的对象中的基本数据类型字段赋默认值或设定值,为非基本类型赋值null,对于static字段只会初始化一次。)

2’ ArithmeticException:算术条件异常。 最常见的就是0作除数时会抛出。

3’ ClassNotFoundException:类未找到异常。 在通过反射Class.forName(“类名”)来获取类时,如果未找到则会抛出异常。

4’ ArrayIndexOutOfBoundsException:数组索引越界异常。 当试图操作数组的索引值为负数或大于等于数组大小时会抛出。

5’ NegativeArraySizeException:数组长度为负值异常。 一般在初始化数组大小为负值时抛出。

6’ ArrayStoreException:数组类型不匹配值异常。 例如将一个Object数组中加入一个Integer对象与一个String对象时,类型不匹配就会抛出。

7’ IllegalArgumentException:非法参数异常。 会在使用Java类库方法时传入参数值越界时抛出。

处理异常

Java中的异常处理原则:必须声明抛出异常或捕获可查异常,允许忽略Error与不可查异常。

public class TestException {
    public static void main(String[] args)
            throws IOException{     //抛出可查IO异常
        //throw是针对对象抛出的异常,throws是针对方法抛出的异常
        FileWriter fileWriter = new FileWriter("output.txt");
        String str = null;          //str对象未经初始化
        try {
            fileWriter.write(str);  //尝试调用未经初始化的str对象,会抛出空指针异常
        } catch (NullPointerException e) {
            System.out.println("Catch NullPointerException!");
        }finally{
            str = "finally!";
            fileWriter.write(str);
            fileWriter.close();
        }   
    }
}

执行结果:
控制台:
Catch NullPointerException!
output.txt:
finally!

1、throws意为抛出,只要出现异常,就会创建对应异常对象,然后记录异常时运行状态等异常信息,交付给运行时系统处理。抛出异常是一定执行在捕获之前的,没有抛出就不会有捕获。程序可以显式使用throws来声明抛出可查异常以通过编译。

2、try-catch-finally异常捕获语句:
try中是可能发生异常的程序段;

catch中依次编写对应的异常处理器方法,当抛出异常后,由运行时系统在栈中从当前位置开始依次回查方法,直到找到合适的异常处理方法,如果未找到,则执行finally或直接结束程序运行。

finally :无论是否捕获或处理异常,finally块里的语句都会被执行。
注意(很重要,面试常问):当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。
在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中抛出了异常且未处理。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)CPU出现异常被关闭

3、try-catch-finally的执行顺序
1’当没有异常捕获时,会跳过catch,直接执行finally块。

public class TestException {
    public static void main(String[] args){         

        try {
            System.out.println("Hello world!");
        } catch (NullPointerException e) {
            System.out.println("Catch NullPointerException!");
        }catch (ArithmeticException e) {
            System.out.println("Catch ArithmeticException!");
        }catch(Exception e){
            System.out.println("Catch other Exception!");
        }
        finally{
            System.out.println("Finally!");
        }   
    }
}

输出结果:
Hello world!
Finally!

2’ 当抛出运行时异常且没有定义相应的异常处理方法,就会由JVM抛出异常。

public class TestException {
    public static void main(String[] args){         
        String str = null;  
        int a = 2, b = 0;
        //调用空对象的方法,会抛出空指针异常
        System.out.println(str.length());       
    }
}

输出结果:
Exception in thread “main” java.lang.NullPointerException
at Java面试.TestException.main(TestException.java:11)

3、当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,最后执行finally语句块后的语句。

public class TestException {
    public static void main(String[] args){         
        String str = null;  
        int a = 2, b = 0;
        try {
            //调用空对象的方法,会抛出空指针异常
            System.out.println(str.length());  //语句1
            //除数为0,会抛出数学运算错误
            System.out.println("a/b=" + a/b);  //语句2
        } catch (NullPointerException e) {
            System.out.println("Catch NullPointerException!");
        }catch (ArithmeticException e) {
            System.out.println("Catch ArithmeticException!");
        }catch(Exception e){
            System.out.println("Catch other Exception!");
        }
        finally{
            System.out.println("Finally!");
        }   
    }
}

输出结果:
Catch NullPointerException!
Finally!

自定义一个异常类

通过继承Exception类实现。

public class TestException {
    public static void main(String[] args){         
        String str = null;  
        int a = 2, b = 0;
        try {
            //调用空对象的方法,会抛出空指针异常
            System.out.println(str.length());  //语句1
            //除数为0,会抛出数学运算错误
            System.out.println("a/b=" + a/b);  //语句2
        } catch (NullPointerException e) {
            System.out.println("Catch NullPointerException!");
        }catch (ArithmeticException e) {
            System.out.println("Catch ArithmeticException!");
        }catch(Exception e){
            System.out.println("Catch other Exception!");
        }
        finally{
            System.out.println("Finally!");
        }   
    }
}

异常链与异常栈轨迹

public class TestException {
    public static void main(String[] args){         
        TestException test = new TestException();
        String str = null;
        test.printStringMessage(str);
    }

    public void printStringMessage(String str){
        System.out.println(str.length());
    }
}

输出结果:
Exception in thread “main” java.lang.NullPointerException
at Java面试.TestException.printStringMessage(TestException.java:16)
at Java面试.TestException.main(TestException.java:12)

常规异常:有Java所定义的异常,不需要异常声明,在未被try-catch的情况下,会被默认上报到main()方法。

异常的冒泡上传机制:当一个异常对象产生了以后,其会按照调用层次(一般是方法的调用层次)进行冒泡,直到被try-catch处理,或上报至main()方法。

/

//自定义的异常类
public class MyException extends Exception{
    String message;                           // 定义String类型变量  
    public MyException(String ErrorMessagr) { // 父类方法  
        message = ErrorMessagr;  
    }  
    public String getMessage() {              // 覆盖getMessage()方法  
        return message;  
    }  
}  
//测试异常链、冒泡机制
public class TestException {
    void firstThrow()
            throws MyException { //抛出自定义异常
        System.out.println("Oringinally creat a MyException and throw it out");
        throw new MyException("MyException"); //真正的抛出异常处
    }
    void secondThrow()
            throws MyException { //抛出自定义异常
        firstThrow();            //调用firstThrow()
    }
    public TestException()
            throws MyException { //构造方法,抛出自定义异常
        secondThrow();           //调用secondThrow()
    }

    public static void main(String[] args) {
        try{
            //调用构造方法
            TestException testException=new TestException();
        }
        catch(MyException e)
        {
            e.printStackTrace();
            System.out.println("Catch a my exception!");
        }
    }
}

输出结果:
Oringinally creat a MyException and throw it out
Java面试.MyException: MyException
at Java面试.TestException.firstThrow(TestException.java:11)
at Java面试.TestException.secondThrow(TestException.java:16)
at Java面试.TestException.(TestException.java:20)
at Java面试.TestException.main(TestException.java:26)
Catch a my exception!

从异常栈的记录信息可以发现,与代码相对应的异常抛出机制和次序:

firstThrow()产生MyException对象->异常冒泡至调用其的secondThrow()->冒泡至调用secondThrow()的TestExceptionChain的构造方法->冒泡至printtry的main()方法。

注意到:异常对象一直被抛出,直至在printtry的mian()方法中被try-catch捕获!

在补上一张异常类的继承图

参考

Java异常的深入研究与分析
JAVA基础——异常详解
Java必知必会:异常机制详解 |强烈推荐,写的非常棒!

java修饰符和this关键子解析

简单明了的的表格介绍

java的四个关键字:public、protected、default、private(他们决定了紧跟其后被定义的东西可以被谁使用)

适用范围<访问权限范围越小,安全性越高>

     访问权限 类 包 子类 其他包

     public ∨ ∨ ∨ ∨ (对任何人都是可用的)

     protect ∨ ∨ ∨ ×    (继承的类可以访问以及和private一样的权限)

     default ∨ ∨ × ×    (包访问权限,即在整个包内均可被访问)

     private ∨ × × ×    (除类型创建者和类型的内部方法之外的任何人都不能访问的元素)

访问控制

private限制构造函数后,其他类中将不能够通过new的方式来获取这个类的对象,能够有效控制对象的创建行为,这在有些设计模式中是非常重要的。

类的修饰符

// public,默认,abstract,final。
public class Test1 {}
class Test2{}
abstract class Test3{}
final class Test4{}

abstract是表示该类为一个抽象类,不能实例化该类,即不能使用Test3 test3 = new Test3();的方式获取Test3的实例。final表示该类不能被子类继承,该类即为最终类,不可再被继承。

接口修饰符

接口比较特殊,接口的修饰符只有public和默认(default),其控制权限和变量完全相同,不再赘述。
值得注意的是,接口中的变量只能为:public static final 变量名。当然了,你也可以不写这些修饰符,编译器会自动帮你加上的,因为编译器默认情况下就是这样修饰的。
接口中的方法,只能用public和abstract修饰符,当然了,你也可以不写这些修饰符,同样,编译器会自动帮你加上的。

this关键字的三种用法

  1. 引用成员变量

    public class ThisDemo {        public static void main(String[] args) {            Student s=new Student("小明明",20);            System.out.println(s);        }    }    class Student{        String name;        int age;        public Student(String name, int age) {            //注:没有加this            name = name;            age = age;        }        @Override        public String toString() {            return "Student [ name=" + name + ", age=" + age +"]";        }    }

    打印结果:Student [ name=null, age=0]
    赋值没有成功,说明:name(局部变量)=name(局部变量);
    而不是name(成员变量)=name(局部变量);

    public Student(String name, int age) {        //注:可以使用this进行区分成员变量和局部变量        this.name = name;        this.age = age;}

    打印结果:Student [ name=小明明, age=20]
    这样子就可以赋值成功啦

  2. 代表当前对象的引用
    this关键字除了可以引用变量或者成员方法之外,还有一个重大的作用就是返回类的引用。如在代码中,可以使用return this,来返回某个类的引用。此时这个this关键字就代表类的名称。如代码在上面student类中,那么代码代表的含义就是return student。可见,这个this关键字除了可以引用变量或者成员方法之外,还可以作为类的返回值,这才是this关键字最引人注意的地方。
    例如:

    public Student(String name, int age) {        //注:可以使用this进行区分成员变量和局部变量        this.name = name;        this.age = age;        //上述两行代码就相当于(假设当前实例对象为s)        {          s.name = name;          s.age = age;        }}```   3. 代表构造器与构造器之间的调用```javaclass Student{        String name;        int age;        int id;        public Student(String name, int age, int id) {            this.name = name;            this.age = age;            this.id = id;            System.out.println("构造器3已调用");        }        public Student(String name, int age) {            this(name,age,0);            System.out.println("构造器2已调用");        }        public Student(String name) {            this(name,0);//参数不足,就使用参数默认值补全            System.out.println("构造器1已调用");        }        @Override        public String toString() {            return "Student [ id=" + id + ", name=" + name + ", age=" + age +"]";        }    }

    //构造器用法–>this();跟函数一样()中可以写参数
    构造器调用一般是这样子用的.不同的构造器中存在相同代码.为了复用性。可以在参数少的构造器中调用参数多的构造器

参考

别说你懂修饰符——深入分析Java修饰符
java中四种访问修饰符区别及详解全过程
java this 3种常见用法 详解
Java中this关键字详解

枚举和JAVA的单例模式

枚举

定义枚举类

package com;

public enum Color {

     RED, GREEN, BLANK, YELLOW

}

简单使用

package com;

public class B {

    public static void main(String[] args) {


        System.out.println( isRed( Color.BLANK ) ) ;  //结果: false
        System.out.println( isRed( Color.RED ) ) ;    //结果: true

    }


    static boolean isRed( Color color ){
        if ( Color.RED.equals( color )) {
            return true ;
        }
        return false ;
    }

}

自定义函数

package com;

public enum Color {

     RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);


    private String name ;
    private int index ;

    private Color( String name , int index ){
        this.name = name ;
        this.index = index ;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getIndex() {
        return index;
    }
    public void setIndex(int index) {
        this.index = index;
    }


}

使用:

package com;

public class B {

    public static void main(String[] args) {

        //输出某一枚举的值
        System.out.println( Color.RED.getName() );
        System.out.println( Color.RED.getIndex() );

        //遍历所有的枚举
        for( Color color : Color.values()){
            System.out.println( color + "  name: " + color.getName() + "  index: " + color.getIndex() );
        }
    }

}

输出结果:

红色  
1  
RED name: 红色 index: 1  
GREEN name: 绿色 index: 2  
BLANK name: 白色 index: 3  
YELLO name: 黄色 index: 4  

除了枚举,亦可以使用 public static final 来实现类似枚举的效果.


# 单例模式单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。

实现方法:
通常情况下,我们写单例模式的时候无非就是三个步骤:构造器私有化,声明私有静态变量,提供静态获取实例的方法。简单说就是以下这种方式(饿汉):

class SingletonA {
    //私有静态实例变量
    private static SingletonA instence = new SingletonA();
    //私有构造器
    private SingletonA() {
    }
    //公有获取静态实例的方法
    public static SingletonA getInstance() {
        return instence;
    }
}

这是最基本的单例模式的写法,考虑到线程安全的问题,会用synchronized 关键字修饰getInstance()方法,另外还有饿汉式、懒汉式、静态内部类、双重校验锁的写法。
但是这种写法存在缺陷,可以利用反射的方式来实例化多个不同的实例.
详细做法参考: 浅谈使用单元素的枚举类型实现单例模式

第一种(懒汉,线程不安全):

public class SingletonDemo1 {
    private static SingletonDemo1 instance;
    private SingletonDemo1(){}
    public static SingletonDemo1 getInstance(){
        if (instance == null) {
            instance = new SingletonDemo1();
        }
        return instance;
    }
}

这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。 

第二种(懒汉,线程安全):

public class SingletonDemo2 {
    //不在这里实例化
    private static SingletonDemo2 instance;
    private SingletonDemo2(){}
    public static synchronized SingletonDemo2 getInstance(){
        if (instance == null) {
            instance = new SingletonDemo2();
        }
        return instance;
    }
}

使用synchronized修饰了getInstance方法.
这种写法在getInstance()方法中加入了synchronized锁。能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是效率很低(因为锁),并且大多数情况下不需要同步。

第三种(饿汉):

public class SingletonDemo3 {
    private static SingletonDemo3 instance = new SingletonDemo3();
    private SingletonDemo3(){}
    public static SingletonDemo3 getInstance(){
        return instance;
    }
}

这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,这时候初始化instance显然没有达到lazy loading的效果。

第四种(饿汉,变种):

public class SingletonDemo4 {
    private static SingletonDemo4 instance = null;
    static{
        instance = new SingletonDemo4();
    }
    private SingletonDemo4(){}
    public static SingletonDemo4 getInstance(){
        return instance;
    }
}

表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance

第五种(静态内部类):

public class SingletonDemo5 {
    private static class SingletonHolder{
        private static final SingletonDemo5 instance = new SingletonDemo5();
    }
    private SingletonDemo5(){}
    public static final SingletonDemo5 getInsatance(){
        return SingletonHolder.instance;
    }
}

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方法就 显得更合理

第六种(枚举):

public enum SingletonDemo6 {
    instance;
    public void whateverMethod(){
    }
}

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

第七种(双重校验锁):

public class SingletonDemo7 {
    private volatile static SingletonDemo7 singletonDemo7;
    private SingletonDemo7(){}
    public static SingletonDemo7 getSingletonDemo7(){
        if (singletonDemo7 == null) {
            synchronized (SingletonDemo7.class) {
                if (singletonDemo7 == null) {
                    singletonDemo7 = new SingletonDemo7();
                }
            }
        }
        return singletonDemo7;
    }
}

这个是第二种方式的升级版,俗称双重检查锁定,详细介绍请查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html
在JDK1.5之后,双重检查锁定才能够正常达到单例效果。

参考

浅谈使用单元素的枚举类型实现单例模式
Java学习整理系列之Java枚举类型的使用
Java 枚举(enum) 详解7种常见的用法
常见的几种单例模式
JAVA设计模式之单例模式