目录
https://docs.oracle.com/javase/8/
https://docs.oracle.com/javase/8/docs/index.html
https://docs.oracle.com/javase/tutorial/index.html
https://docs.oracle.com/javase/8/docs/api/
The javac compiler has a -profile option
AccessController.doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context, Permission... perms) throws PrivilegedActionException
能够代码断言 特权的子集,不用遍历stack去校验其他权限.java.security.cert.PKIXRevocationChecker
类配置 废弃X.509证书校验jjs
命令调用Nashorn 引擎(一种JavaScript 引擎,已废弃)java
命令可启动 javaFX 应用java
命令手册已经重写jdeps
命令分析类文件Nashorn Javascript Engine 替代 Rhino javascript engine
java.lang.String(byte[], *)
constructor and thejava.lang.String.getBytes()
method 性能提升JDK 8 包含了 Java DB 10.10.
java.net.URLPermission
类java.net.HttpURLConnection
类中,如果安装了 security manager ,那么需要权限才能调用请求java.util.concurrent
包新增了类和接口java.util.concurrent.ConcurrentHashMap
新增了方法支持 聚集操作java.util.concurrent.atomic
新增类java.util.concurrent.ForkJoinPool
新增方法支持 common pooljava.util.concurrent.locks.StampedLock
类硬件内部函数新增 AES ,使用 选项开启.
-XX:+UseAES -XX:+UseAESIntrinsics
移除PermGen,永久代
方法调用字节码指令 支持 默认方法.
关系图如下:
原图地址:https://docs.oracle.com/javase/8/docs/index.html
对象是理解面向对象技术的关键.
真实世界的对象有2个特征: 状态(state)和行为(behavior).
软件对象和真实世界对象很相似.都是由2部分组成: state 和 关联的behavior.
对象使用 field 存储state , 通过 methods 暴露行为, Methods 操作 对象内部的state,服务于对象之间的通信,
如下图:
Object 是class 的一个实例.类是从对象中抽象出来的.将许多对象的共同的属性(state) 和方法()抽取出来形成1个 class.
class Bicycle {
int cadence = 0;
int speed = 0;
int gear = 1;
void changeCadence(int newValue) {
cadence = newValue;
}
void changeGear(int newValue) {
gear = newValue;
}
void speedUp(int increment) {
speed = speed + increment;
}
void applyBrakes(int decrement) {
speed = speed - decrement;
}
void printStates() {
System.out.println("cadence:" +
cadence + " speed:" +
speed + " gear:" + gear);
}
}
面向对象编程允许一个类去继承另一个类的state and behavior 去使用.每个 类只能有1个父类,每个父类可以有多个子类.
继承是通过关键字extends
实现的.
// MountainBike 继承了 Bicycle
class MountainBike extends Bicycle {
// new fields and methods defining
// a mountain bike would go here
}
接口就是相关的方法(没有方法体)集合.
interface Bicycle {
// wheel revolutions per minute
void changeCadence(int newValue);
void changeGear(int newValue);
void speedUp(int increment);
void applyBrakes(int decrement);
}
实现接口通过关键字 implements
体现.
// ACMEBicycle 实现了 Bicycle 接口
class ACMEBicycle implements Bicycle {
int cadence = 0;
int speed = 0;
int gear = 1;
// The compiler will now require that methods
// changeCadence, changeGear, speedUp, and applyBrakes
// all be implemented. Compilation will fail if those
// methods are missing from this class.
void changeCadence(int newValue) {
cadence = newValue;
}
void changeGear(int newValue) {
gear = newValue;
}
void speedUp(int increment) {
speed = speed + increment;
}
void applyBrakes(int decrement) {
speed = speed - decrement;
}
void printStates() {
System.out.println("cadence:" +
cadence + " speed:" +
speed + " gear:" + gear);
}
}
接口形成了类与外界的连接,这种连接在构建时期由编译器执行.一个类如果实现接口,那么接口里的所有方法必须实现.
Java 8 支持接口类中的方法提供默认实现.
包是组织一组相关类和接口的命名空间.类似于电脑上的文件夹.
Java 8 API 文档:https://docs.oracle.com/javase/8/docs/api/index.html
Java 编程语言提供了以下几种变量:
实例变量 Instance Variables (Non-Static Fields)
成员变量(无 static 关键字修饰)
类变量 Class Variables (Static Fields)
static 关键字修饰的 成员变量
本地变量 Local Variables
方法内部声明的变量
参数变量 Parameters
方法的参数变量
变量名:以字母,$
符号或下划线(_
) 开头.首字母之后的字符可以是字母.数字,$
或 _
变量名最好用全称,别用简写!
一个单词的变量名使用全部小写;第2个单词和以后的单词的首字母需要大写,其余小写;
常量多个字母间使用下划线(_
)拼接,所有字母大写.
byte
8-bit, 取值范围 -128 到 127(包含127) ( -2的 7次方 到 2的 7次方-1)
short
16-bit,2个字节, 取值范围- 32,768 到 32,767 (包含32767)
int
32-bit , 4个字节, 取值范围 -2的 31次方 到 2的 31次方-1
long
64-bit , 8个字节, 取值范围 -2的 63次方 到 2的 63次方-1
float
单精度浮点类型,32-bit (如果需要更高精度,则 可使用 java.math.BigDecimal
替换)
double
双精度浮点类型,64-bit(如果需要更高精度,则 可使用 java.math.BigDecimal
替换)
boolean
true 或 false
char
16-bit Unicode 字符, \u0000
到 \uffff
默认值
下图是有10个元素的数组
声明一个数组:
// declares an array of integers
int[] anArray;
实例化一个数组:
int[] anArray = new int[10];
实例化一个数组,并初始化元素值
int[] anArray = {1,2,3,4,5,6,7,8,9,10};
拷贝数组:
class ArrayCopyDemo {
public static void main(String[] args) {
String[] copyFrom = {
"Affogato", "Americano", "Cappuccino", "Corretto", "Cortado",
"Doppio", "Espresso", "Frappucino", "Freddo", "Lungo", "Macchiato",
"Marocchino", "Ristretto" };
String[] copyTo = new String[7];
System.arraycopy(copyFrom, 2, copyTo, 0, 7);
for (String coffee : copyTo) {
System.out.print(coffee + " ");
}
}
}
遍历数组:
class ArrayCopyOfDemo {
public static void main(String[] args) {
String[] copyFrom = {
"Affogato", "Americano", "Cappuccino", "Corretto", "Cortado",
"Doppio", "Espresso", "Frappucino", "Freddo", "Lungo", "Macchiato",
"Marocchino", "Ristretto" };
String[] copyTo = java.util.Arrays.copyOfRange(copyFrom, 2, 9);
for (String coffee : copyTo) {
System.out.print(coffee + " ");
}
}
}
java.util.Arrays
工具类提供了操作数组的方法,比如:
binarySearch
方法查找元素
equals
比较两个数组
fill
使用特定值填充数组每个元素
sort
按升序排列数组
stream
使用流遍历处理数组
java.util.Arrays.stream(copyTo).map(coffee -> coffee + " ").forEach(System.out::print);
toString
将数组转为 String
注意:
*
not used
**
added in 1.2
***
added in 1.4
****
added in 5.0
图片地址:https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html
表达式 是由 变量,操作符和方法调用组成.
(x + y) / 100 // unambiguous, recommended
语句 : 完整的执行单元,以分号结尾的
表达式语句:
// assignment statement
aValue = 8933.234;
// increment statement
aValue++;
// method invocation statement
System.out.println("Hello World!");
// object creation statement
Bicycle myBike = new Bicycle();
代码块:由一组大括号包含的0到多个语句组成.
class BlockDemo {
public static void main(String[] args) {
boolean condition = true;
if (condition) { // begin block 1
System.out.println("Condition is true.");
} // end block one
else { // begin block 2
System.out.println("Condition is false.");
} // end block 2
}
}
if-then-else
switch ( 1.7 开始,支持String)
public static int getMonthNumber(String month) {
int monthNumber = 0;if (month == null) {
return monthNumber;
}
switch (month.toLowerCase()) {
case "january":
monthNumber = 1;
break;
case "february":
monthNumber = 2;
break;
case "march":
monthNumber = 3;
break;
case "april":
monthNumber = 4;
break;
case "may":
monthNumber = 5;
break;
case "june":
monthNumber = 6;
break;
case "july":
monthNumber = 7;
break;
case "august":
monthNumber = 8;
break;
case "september":
monthNumber = 9;
break;
case "october":
monthNumber = 10;
break;
case "november":
monthNumber = 11;
break;
case "december":
monthNumber = 12;
break;
default:
monthNumber = 0;
break;
}
return monthNumber;
}
}
while 和 do … while
while (expression) {
statement(s)
}
do {
statement(s)
} while (expression);
for 循环
for (initialization; termination;
increment) {
statement(s)
}
class EnhancedForDemo {
public static void main(String[] args){
int[] numbers =
{1,2,3,4,5,6,7,8,9,10};
for (int item : numbers) {
System.out.println("Count is: " + item);
}
}
}
break (跳出循环)
continue(跳过本次循环,继续下次循环)
return(返回)
// 有返回值方法
return ++count;
// void 方法
return;
声明一个类:
/*
修饰符 class关键字 类名 extends 父类名 implements 接口名(多个){
}
*/
class MyClass extends MySuperClass implements YourInterface {
// field, constructor, and
// method declarations
}
声明成员变量:
// 访问修饰符 类型 变量名
public int cadence;
public int gear;
public int speed;
定义方法:
/*
访问修饰符 返回值 方法名(参数类型 参数名, ...){
}
*/
public double calculateAnswer(double wingSpan, int numberOfEngines,
double length, double grossTons) {
//do the calculation here
}
方法重载:
Java 可以通过方法签名判断方法是否不同,Java 支持一个类中存在方法名相同,参数列表不同的方法.参数列表不同的含义是:方法参数类型不同 或者 方法参数个数不同
// 存在多个重载方法的draw 方法
public class DataArtist {
...
public void draw(String s) {
...
}
public void draw(int i) {
...
}
public void draw(double f) {
...
}
public void draw(int i, double f) {
...
}
}
注意:尽量克制使用方法重载,过多的使用会降低可读性.
构造器
一个类可以包含多个构造器,构造器在创建类的一个对象时被调用.
如果不声明构造器,那么编译器会自动生成一个无参的构造器.
构造器和普通方法很像,但是,构造器不能有返回类型且构造器名必须和类名一致!
/*
修饰符 类名(参数列表...){
构造器方法体
}
*/
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
任意数量的参数
// polygonFrom方法有任意数量且类型为Point的变量
public Polygon polygonFrom(Point... corners) {
int numberOfSides = corners.length;
double squareOfSide1, lengthOfSide1;
squareOfSide1 = (corners[1].x - corners[0].x)
* (corners[1].x - corners[0].x)
+ (corners[1].y - corners[0].y)
* (corners[1].y - corners[0].y);
lengthOfSide1 = Math.sqrt(squareOfSide1);
// more method body code follows that creates and returns a
// polygon connecting the Points
}
基本类型参数传递的是具体值;引用类型传递的是引用地址.
this关键字
在一个实例的方法或构造器中, this
关键字是一个当前对象的引用.
成员变量:
public class Point {
public int x = 0;
public int y = 0;
//constructor
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
使用构造器:
public class Rectangle {
private int x, y;
private int width, height;
public Rectangle() {
this(0, 0, 1, 1);
}
public Rectangle(int width, int height) {
this(0, 0, width, height);
}
public Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
类成员变量的访问级别
有2种类型的访问级别:
理解类 成员
对象.方法
访问.常量
// 常量命名:字母必须全部大写,且多个单词用下划线连接
static final double PI = 3.141592653589793;
初始化成员
public class BedAndBreakfast {
// initialize to 10
public static int capacity = 10;
// initialize to false
private boolean full = false;
}
静态初始化块
多个静态初始化块,按声明的先后顺序进行初始化.
static {
// whatever code is needed for initialization goes here
}
或者
class Whatever {
public static varType myVar = initializeClassVariable();
private static varType initializeClassVariable() {
// initialization code goes here
}
}
初始化块
java 编译器,将初始化块复制到每个构造器中
{
// whatever code is needed for initialization goes here
}
final 方法:
class Whatever {
private varType myVar = initializeInstanceVariable();
// final 方法,不能被子类重写
protected final varType initializeInstanceVariable() {
// initialization code goes here
}
}
class OuterClass {
...
// 内部类
class InnerClass {
...
}
// 静态内部类
static class StaticNestedClass {
...
}
}
内部类 实例化:
// 先实例化外部类,再实例化内部类
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
OuterClass.java
public class OuterClass {
String outerField = "Outer field";
static String staticOuterField = "Static outer field";
class InnerClass {
void accessMembers() {
System.out.println(outerField);
System.out.println(staticOuterField);
}
}
static class StaticNestedClass {
void accessMembers(OuterClass outer) {
// Compiler error: Cannot make a static reference to the non-static
// field outerField
// System.out.println(outerField);
System.out.println(outer.outerField);
System.out.println(staticOuterField);
}
}
public static void main(String[] args) {
System.out.println("Inner class:");
System.out.println("------------");
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
innerObject.accessMembers();
System.out.println("\nStatic nested class:");
System.out.println("--------------------");
StaticNestedClass staticNestedObject = new StaticNestedClass();
staticNestedObject.accessMembers(outerObject);
System.out.println("\nTop-level class:");
System.out.println("--------------------");
TopLevelClass topLevelObject = new TopLevelClass();
topLevelObject.accessMembers(outerObject);
}
}
public class TopLevelClass {
void accessMembers(OuterClass outer) {
// Compiler error: Cannot make a static reference to the non-static
// field OuterClass.outerField
// System.out.println(OuterClass.outerField);
System.out.println(outer.outerField);
System.out.println(OuterClass.staticOuterField);
}
}
内部类的序列化强烈不推荐使用.
本地类
可以在任意一个代码块中,声明一个类.
public class LocalClassExample {
static String regularExpression = "[^0-9]";
public static void validatePhoneNumber(
String phoneNumber1, String phoneNumber2) {
final int numberLength = 10;
// Valid in JDK 8 and later:
// int numberLength = 10;
class PhoneNumber {
String formattedPhoneNumber = null;
PhoneNumber(String phoneNumber){
// numberLength = 7;
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
public String getNumber() {
return formattedPhoneNumber;
}
// Valid in JDK 8 and later:
// public void printOriginalNumbers() {
// System.out.println("Original numbers are " + phoneNumber1 +
// " and " + phoneNumber2);
// }
}
PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
// Valid in JDK 8 and later:
// myNumber1.printOriginalNumbers();
if (myNumber1.getNumber() == null)
System.out.println("First number is invalid");
else
System.out.println("First number is " + myNumber1.getNumber());
if (myNumber2.getNumber() == null)
System.out.println("Second number is invalid");
else
System.out.println("Second number is " + myNumber2.getNumber());
}
public static void main(String... args) {
validatePhoneNumber("123-456-7890", "456-7890");
}
}
匿名类
public class HelloWorldAnonymousClasses {
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
public void sayHello() {
class EnglishGreeting implements HelloWorld {
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting();
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
HelloWorld spanishGreeting = new HelloWorld() {
String name = "mundo";
public void greet() {
greetSomeone("mundo");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hola, " + name);
}
};
englishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
spanishGreeting.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp =
new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}
匿名内部类访问外部类本地变量限制(https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html#accessing):
final
关键字修饰的变量);Lambda表达式
语法:
// 格式: 小括号包围的逗号分隔的参数列表 + "->" + 方法体
// Java 会自动评估返回值
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
// 或者
// p 代表方法参数类型的实例,下面示例的方法只有1个参数
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
注:不推荐序列化 lambda表达式.
lambda方法引用 4种类型:
什么时候使用内部类,本地类,匿名类,Lambda表达式?
链接:https://docs.oracle.com/javase/tutorial/java/javaOO/whentouse.html
本地类:当创建一个类的多个实例,访类的构造方法.
匿名内部类:如果需要声明成员变量或额外的方法时使用.
lambda表达式:(1) 当你想将单一的行为传给其他代码去执行 (2) 创建函数接口简单实例时
内部类:和本地类需求一样,但是不要求访问外部类的本地变量和方法参数
创建对象:
Point originOne = new Point(23, 94);
创建对象语句,包含了3部分:
(1) 声明变量:比如上边的 Point originOne.
(2) 实例化对象: new
关键字是创建对象的操作符.
(3) 初始化对象: new
操作符后紧跟着调用构造器方法进行初始化对象.
垃圾回收
一些面向对象语言要求你跟踪对象,当对象不再使用的时候进行销毁.
Java 允许你创建足够的对象,不用担心销毁他们,Java 运行环境当发现对象不再使用时,自动删除对象,这样的过程叫垃圾回收.
垃圾收集器自动回收释放对象占用的空间.
enum 类型是一个特殊的数据类型,能够让变量预定义一些常量集合.
enum 类型成员是大写字母.
// 简单枚举
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
测试:
public class EnumTest {
Day day;
public EnumTest(Day day) {
this.day = day;
}
public void tellItLikeItIs() {
switch (day) {
case MONDAY:
System.out.println("Mondays are bad.");
break;
case FRIDAY:
System.out.println("Fridays are better.");
break;
case SATURDAY: case SUNDAY:
System.out.println("Weekends are best.");
break;
default:
System.out.println("Midweek days are so-so.");
break;
}
}
public static void main(String[] args) {
EnumTest firstDay = new EnumTest(Day.MONDAY);
firstDay.tellItLikeItIs();
EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
thirdDay.tellItLikeItIs();
EnumTest fifthDay = new EnumTest(Day.FRIDAY);
fifthDay.tellItLikeItIs();
EnumTest sixthDay = new EnumTest(Day.SATURDAY);
sixthDay.tellItLikeItIs();
EnumTest seventhDay = new EnumTest(Day.SUNDAY);
seventhDay.tellItLikeItIs();
}
}
// 带构造器枚举类
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
private double mass() { return mass; }
private double radius() { return radius; }
// universal gravitational constant (m3 kg-1 s-2)
public static final double G = 6.67300E-11;
double surfaceGravity() {
return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: java Planet <earth_weight>");
System.exit(-1);
}
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf("Your weight on %s is %f%n",
p, p.surfaceWeight(mass));
}
}
注解,元数据(我理解是一个配置信息)的一种形式,提供关于程序的数据,但注解并不属于程序.
Annotations 有很多用途,:
注解用在哪里?
注解应用于声明:类,成员变量,方法和其他程序元素.
JAVA 8 支持注解应用在类型使用的地方,比如:
声明一个注解
通过 @interface
来定义一个注解类:
@interface ClassPreamble {
String author();
String date();
int currentRevision() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// Note use of array
String[] reviewers();
}
@ClassPreamble (
author = "John Doe",
date = "3/17/2002",
currentRevision = 6,
lastModified = "4/12/2004",
lastModifiedBy = "Jane Doe",
// Note array notation
reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {
// class code goes here
}
Java 提供的预定义注解
元注解
Java 提供的标注在其他注解上的注解
@Retention: 表示标记的注解怎么存储,
@Documented : 表示改注解将在Java Doc 中保留
@Target : 注解标注的位置
@Inherited:子类可以从父类上标注的注解继承
@Repeatable: 同一注解可以在同一位置可以标注多个
Java 8 以前,注解只能在声明的时候使用,现在从 Java 8 开始可以在类使用的地方.
Repeating 注解
可重复注解,使用方式如下:
@Alert(role="Manager")
@Alert(role="Administrator")
public class UnauthorizedAccessException extends SecurityException { ... }
Java 编译器会自动将可重复注解存储到一个容器注解中, 为了让Java 编译器能够实现把可重复注解存到容器注解中,必须在代码里声明以下2个类:
声明一个可重复注解@Schedule
import java.lang.annotation.Repeatable;
// Schedules.class 是容器注解类型,用于存储 可重复注解 @Schedule
@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}
声明一个容器注解 @Schedules
public @interface Schedules {
// 必须存在一个 value 属性,类型为 可重复注解
Schedule[] value();
}
以上,是指定了容器注解类型,但是如果没有指定,那么 会报错.
例子:
获取标注的注解示例:
// 通过
AnnotatedElement.getAnnotationsByType(Class<T>)
测试:
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>注释注解<p/>
* <p>特点:<p/>
* <li>仅保留在程序运行阶段(可重复注解的保留阶段应包含 CommentContainer的保留阶段 )</li>
* <li>可重复注解,多个注解存储在 CommentContainer的 value()中</li>
* <li>可标注在 class 和 interface上.</li>
* @author limo
* @date 2022-01-10 18:43:027
*/
@Retention(RetentionPolicy.CLASS)
@Repeatable(CommentContainer.class)
@Target({ElementType.TYPE})
public @interface Comment {
// 作者
String auth();
// 日期
String date();
// 版本
String version();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* {@code @Comment} 注解的容器注解,用于存储多个 {@code @Comment} 注解的数据
* <p>特点:<p/>
* <li>仅保留在程序运行阶段(可重复注解的保留阶段应包含 CommentContainer的保留阶段 )</li>
* <li>可标注在 class 和 interface上.(需包含 可重复注解{@code @Comment} 的@Target)</li>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE,ElementType.TYPE})
public @interface CommentContainer {
// 必须有 value 属性,其存储类型为可重复注解@Comment的数组
Comment[] value();
}
@Comment(auth = "limo", date = "2021-01-10", version = "v1.0版本")
@Comment(auth = "black", date = "2022-02-20", version = "v2.0版本")
public class Demo {
public static void main(String[] args) {
Class demoClass = Demo.class;
// 通过 getAnnotation(CommentContainer.class) 获取标注的 @Comment 注解的值
// 如果标注多个 @Comment 注解,此方法会报异常
try {
System.out.println("================demoClass.getAnnotation(Comment.class)===============================");
Comment limoComment = (Comment) demoClass.getAnnotation(Comment.class);
System.out.println("demo 的注释:");
System.out.println("\t作者:" + limoComment.auth());
System.out.println("\t时间:" + limoComment.date());
System.out.println("\t版本:" + limoComment.version());
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(1L);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// 通过 getAnnotation(CommentContainer.class) 获取标注的每个 @Comment
// 如果有 1个 @Comment注解,,此方法会报异常
System.out.println(
"================demoClass.getAnnotation(CommentContainer.class)===============================");
try {
CommentContainer commentContainer = (CommentContainer) demoClass.getAnnotation(CommentContainer.class);
Comment[] commentContainerValue = commentContainer.value();
for (Comment c : commentContainerValue) {
System.out.println("demo 的注释:");
System.out.println("\t作者:" + c.auth());
System.out.println("\t时间:" + c.date());
System.out.println("\t版本:" + c.version());
}
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(1L);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// getAnnotationsByType(Comment.class) 获取标注的每个 @Comment 注解
// 如果有没有@Comment注解或者多个@Comment注解,此方法不会报异常
System.out.println(
"================demoClass.getAnnotationsByType(Comment.class)===============================");
try {
Comment[] comments = (Comment[]) demoClass.getAnnotationsByType(Comment.class);
for (Comment c : comments) {
System.out.println("demo 的注释:");
System.out.println("\t作者:" + c.auth());
System.out.println("\t时间:" + c.date());
System.out.println("\t版本:" + c.version());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试结果:
================demoClass.getAnnotation(Comment.class)===============================
demo 的注释:
java.lang.NullPointerException
at com.untouch.hdmap.uaa.annotation.Demo.main(Demo.java:16)
================demoClass.getAnnotation(CommentContainer.class)===============================
demo 的注释:
作者:limo
时间:2021-01-10
版本:v1.0版本
demo 的注释:
作者:black
时间:2022-02-20
版本:v2.0版本
================demoClass.getAnnotationsByType(Comment.class)===============================
demo 的注释:
作者:limo
时间:2021-01-10
版本:v1.0版本
demo 的注释:
作者:black
时间:2022-02-20
版本:v2.0版本
经测试,我推荐使用 getAnnotationsByType(Comment.class)
代码获取标注的多个@Comment
注解时, 不管你有没有使用@Comment
注解都不会报异常.
灵活使用 @Repeatable 注解和 @Target 能设计出更灵活的注解.
定义接口
使用关键子 interface
定义一个接口类
public interface GroupedInterface extends Interface1, Interface2, Interface3 {
// 接口体可以包含: 抽象方法,默认方法,静态方法
// constant declarations
// base of natural logarithms
double E = 2.718282;
// method signatures(无方法体)
void doSomething (int i, double x);
int doSomethingElse(String s);
}
一个类通过 implements
关键字来实现接口.
public interface Relatable {
// this (object calling isLargerThan)
// and other must be instances of
// the same class returns 1, 0, -1
// if this is greater than,
// equal to, or less than other
public int isLargerThan(Relatable other);
}
public class RectanglePlus
implements Relatable {
public int width = 0;
public int height = 0;
public Point origin;
// four constructors
public RectanglePlus() {
origin = new Point(0, 0);
}
public RectanglePlus(Point p) {
origin = p;
}
public RectanglePlus(int w, int h) {
origin = new Point(0, 0);
width = w;
height = h;
}
public RectanglePlus(Point p, int w, int h) {
origin = p;
width = w;
height = h;
}
// a method for moving the rectangle
public void move(int x, int y) {
origin.x = x;
origin.y = y;
}
// a method for computing
// the area of the rectangle
public int getArea() {
return width * height;
}
// a method required to implement
// the Relatable interface
public int isLargerThan(Relatable other) {
RectanglePlus otherRect
= (RectanglePlus)other;
if (this.getArea() < otherRect.getArea())
return -1;
else if (this.getArea() > otherRect.getArea())
return 1;
else
return 0;
}
}
接口扩展
如果接口设计的不合理,需要新增方法,那么接口的双方都需要重新调整接口;可通过新增接口继承原接口,并添加新增的方法扩展接口:
// 源接口
public interface DoIt {
void doSomething(int i, double x);
int doSomethingElse(String s);
}
// 新接口,扩展了源接口,并新增 didItWork 方法
public interface DoItPlus extends DoIt {
boolean didItWork(int i, double x, String s);
}
还可以新增 default 方法,代码如下:
public interface DoIt {
void doSomething(int i, double x);
int doSomethingElse(String s);
// 默认方法,接口实现类并不需要覆盖
default boolean didItWork(int i, double x, String s) {
// Method body
}
}
测试
/**
* TV接口
* @author black
* @date 2022-01-11 12:16:034
*/
public interface TV {
// 名字
String name();
// 生产日期
String date();
// 信息
default void print() {
System.out.println("品牌:未知");
}
}
/**
* 小米电视
* @author black
* @date 2022-01-11 12:16:008
*/
public class MiTV implements TV{
@Override
public String name() {
return "MIA40TV";
}
@Override
public String date() {
return "2022-01-11 12:17:57";
}
public void print() {
System.out.println("品牌:小米TV");
// 如果想使用接口中定义的默认方法,则 使用 TV.super.print()调用
}
}
/**
* 测试类
* @author black
* @date 2022-01-11 12:15:056
*/
public class TVTest {
public static void main(String[] args) {
TV t = new MiTV();
t.print();
}
}
测试结果:
品牌:小米TV
默认方法可以覆盖:
/**
* TV接口
* @author black
* @date 2022-01-11 12:16:034
*/
public interface TV {
// 名字
String name();
// 生产日期
String date();
// 信息
default void print() {
System.out.println("品牌:未知");
}
}
public interface TVPlus extends TV {
// 重写 父接口的 name方法
@Override
default String name(){
System.out.println("TvPlus");
}
// 覆盖父接口的 print 默认方法
@Override
default void print() {
// TODO Auto-generated method stub
System.out.println("TvPlus");
}
}
从原始类衍生出来的类叫做子类,原始类就叫父类(超类,基类).
子类继承父类所有所有(非私)成员(除了构造器).
java.lang.Object 类定义了所有类的通用行为的实现,Java 平台中所有类都是 Object类的后代.
类型猜测
// 父类声明指向子类实现
Object obj = new MountainBike();
// 如何判断并获取子类对象实例?
(1) instanceof 判断
(2) 强转
if (obj instanceof MountainBike) {
MountainBike myBike = (MountainBike)obj;
}
一个类只能继承1个父类,但是可以实现多个接口.
多态
父类声明指向子类实例,同一行为的表现结果不一样.
super关键字
super 代表父类实例,通过 super 关键字调用父类的属性和方法.
Object 类方法说明
notify() , notifyAll(), wait(),wait(long timeout) 方法跟 synchronize 同步器配合使用.
final关键字
final 修饰的类不允许子类继承,修饰的方法不允许子类重写.
抽象类
使用 abstract 关键字定义一个抽象类或抽象方法.
抽象类不能被实例化, 必须指向子类实例. abstract 关键字不能修饰变量.
public abstract class GraphicObject {
// declare fields
// declare nonabstract methods
// 抽象方法可以没有方法体,其子类必须实现此方法.
abstract void draw();
}
如果抽象类被继承,那么抽象类所有的抽象方法,子类必须实现全部.
接口里的方法可以被修饰为 abstract 修饰,但是没有必要,因为接口里的方法,可以没有方法体.
抽象类和接口比较
什么时候该使用抽象类?
什么时候该使用接口?
抽象类实现接口
abstract class X implements Y {
// 可仅实现 Y接口的一个方法
}
class XX extends X {
// 实现Y剩余的方法
}
Java 为了使用对象替换基本类型,提供了包装类.
所有包装类的父类都是 Number 类的子类:
格式化输出
public PrintStream format(String format, Object... args)
方法进行格式化输出.System.out 其实就是 PrintStream类.
格式字符串语法文档: https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax
格式字符串有以下语法:
(1) 一般格式化(字符,数值格式化)语法:
%[argument_index$][flags][width][.precision]conversion
(2) 针对于日期和时间格式化语法:
%[argument_index$][flags][width]conversion
(3)没有参数列表的格式化语法:
%[flags][width]conversion
Conversion 列表:
转换
参数类别
描述
'b', 'B'
general
如果参数为null,则返回"false" ;如果参数是布尔类型,则返回String.valueOf(arg);否则,返回"true"
'h', 'H'
general
如果参数为null,则返回"null" ; 否则结果是 Integer.toHexString(arg.hashCode())
's', 'S'
general
如果参数为null,则返回"null" ; 如果 参数实现了Formattable 接口,则调用参数的 formatTo方法;否则调用参数的 toString()方法
'c', 'C'
general
Unicode 字符
'd'
general
结果被格式化为十进制整数
'o'
general
结果被格式化为 八进制 整型
'x', 'X'
general
结果被格式化为 十六进制 整型
'e', 'E'
general
结果被格式化为计算机科学记数法中的十进制数
'f'
general
结果被格式化为浮点型
'g', 'G'
general
结果采用计算机化的科学记数法或十进制格式,取决于精度和四舍五入的值
'a', 'A'
general
结果被格式化为具有有效值和指数的十六进制浮点数 ,不支持BigDecimal
't', 'T'
date/time
日期转换前缀,参见日期转换列表
'%'
percent
百分号
'n'
general
行分割符,跟平台有关
时间转换符
转换
说明
'H'
24小时格式,00 ,01,02,03,…, 23
'I'(大写 哎)
12小时格式,01 ~ 12
'k'
24小时格式,0,1,2,3… ~ 23
'l' (小写诶哦)
12小时格式,1 ~ 12
'M'
分钟 00 ~ 19
'S'
秒 00 ~ 60
'L'
毫秒 000 ~ 999
'N'
纳秒 000000000 ~ 999999999
'p'
上午下午 am 或 pm
'z'
RFC 822 style numeric time zone offset from GMT, e.g. -0800.
'Z'
表示时区缩写的字符串
's'
从 1970年1月1日 00:00:00 到现在的秒数, Long.MIN_VALUE/1000 to Long.MAX_VALUE/1000
'Q'
从 1970年1月1日 00:00:00 到现在的毫秒数, Long.MIN_VALUE to Long.MAX_VALUE
日期转换符
转换符
说明
'B'
月,英文单词全拼
'b'
月,英文单词缩写
'h'
月,英文单词缩写
'A'
周一周二等的英文单词全拼,Sunday
'a'
周一周二等的英文单词全拼,Sun
'C'
4位年整除 100后的值,00~99
'Y'
年 (4位)
'y'
4位年最后2位
'j'
年的天,001 - 366
'm'
月,01 - 13
'd'
月的天 01 - 31
'e'
月的天 1 - 31
Flag 列表
flag
说明
'-'
左对齐
'#'
结果应该使用依赖于转换的替代形式
'+'
结果总包含一个符号
' '
结果将包括一个前导空格以表示正值
'0'
结果将用零填充
','
结果将包括特定于区域设置的分组分隔符
'('
结果将在括号中包含负数
java.text.DecimalFormat
import java.text.*;
public class DecimalFormatDemo {
static public void customFormat(String pattern, double value ) {
DecimalFormat myFormatter = new DecimalFormat(pattern);
String output = myFormatter.format(value);
System.out.println(value + " " + pattern + " " + output);
}
static public void main(String[] args) {
customFormat("###,###.###", 123456.789);
customFormat("###.##", 123456.789);
customFormat("000000.000", 123.78);
customFormat("$###,###.###", 12345.67);
}
}
自动装箱拆箱
基本类型和其包装类可以直接赋值使用,并不进行强转.
// 自动装箱
Boolean b = true;
Integer i = 1;
Double d = 0.2d;
Float f = 0.3f;
// 自动拆箱
boolean bb = b;
int ii = i;
double dd = d;
float ff = f;
为什么使用泛型?
泛型类
/**
* Generic version of the Box class.
* @param <T> the type of the value being boxed
*/
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
泛型类型命名规则
单个大写字母
常见的泛型类型:
调用和实例化泛型类型
Box<Integer> integerBox = new Box<Integer>();
从 JDK7 开始,只要Java 编译器能够根据上下文决定类型,那么就可以使用空菱形<>
代替实例化时的类型参数:
Box<Integer> integerBox = new Box<>();
多类型参数的泛型类
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
原生类型
声明和实例化时不指定具体类型,就是原生类型:
// Box 是个泛型类,实例化时并不执行泛型类型,此时被称为原生类型
Box rawBox = new Box();
泛型方法
泛型方法,就是说明了方法参数类型的方法.
public class Util {
// compare方法参数有2个泛型类型分别是 K和V
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
泛型的边界
泛型中使用 extends
关键字限制使用类型的边界. 泛型extends
关键字同时包含继承
和实现
语义.
// 单个界限
public class Box<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
public <U extends Number> void inspect(U u){
System.out.println("T: " + t.getClass().getName());
System.out.println("U: " + u.getClass().getName());
}
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
integerBox.set(new Integer(10));
integerBox.inspect("some text"); // error: this is still String!
}
}
多个边界:
// <T extends B1 & B2 & B3>
Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }
class D <T extends A & B & C> { /* ... */ }
// 如果 A 不在前边的话,编译会报错
// 比如:class D <T extends B & A & C> { /* ... */ } // compile-time error
通过限定泛型边界可对泛型参数进行算数:
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
// 如果没有 extends Comparable ,那么 e 和 elem 就无法比较(使用大于小于都不行),编译就会报错
if (e.compareTo(elem) > 0)
++count;
return count;
}
通配符
在泛型里,问号"?"通配符表示未知类型,
类型擦除
为了实现泛型,Java 编译器进行了以下方面的类型擦除:
泛型的限制
不能使用基本类型实例化泛型类
class Pair
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
// ...
}
Pair
不能实例化类型参数
public static
E elem = new E(); // compile-time error
list.add(elem);
}
不能声明静态属性
public class MobileDevice
private static T os;
// ...
}
// 如果允许泛型用在静态变量,那么下面对象中os属性的类型到底是什么?
MobileDevice
MobileDevice
MobileDevice
不能对参数化类型使用类型转换或instanceof
public static
if (list instanceof ArrayList
// …
}
}
List
List
不能创建泛型数组
List
不能创建、捕获或抛出参数化类型的对象
// Extends Throwable indirectly
class MathException
// Extends Throwable directly
class QueueFullException
不能重载方法,其中每个重载的形式参数类型擦除为相同的原始类型
public class Example {
public void print(Set
public void print(Set
}
包名命名规则
使用包里的类
全限定名
// 使用 graphics包下的 Rectangle 类
graphics.Rectangle myRect = new graphics.Rectangle();
使用import
关键字导入后使用
//
import graphics.Rectangle;
Rectangle rc = new Rectangle();
// 静态导入
import static java.lang.Math.*;
double r = cos(PI * theta);
什么是异常?
异常是程序执行发生的一个扰乱正常流程的事件.
异常调用栈:
3种类型的异常:
使用 try-catch-finally 代码块处理异常.
try {
code
} catch (ExceptionType name) {
} catch (ExceptionType name) {
}
当try 包围的代码抛出异常时,会查找相关联的异常处理器.每一个 catch 块是一个异常处理器,catch 捕获的异常类型均继承自 Throwable
类.
JDK 1.7 开始 ,catch可同时捕获多个异常:
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
当 try 块退出的的时候,finally块肯定会执行(如果在执行 try块过程中 JVM退出,那么 finally 不会执行.).
finally {
if (out != null) {
System.out.println("Closing PrintWriter");
out.close();
} else {
System.out.println("PrintWriter not open");
}
}
finally 块是防止资源泄露的关键工具.
try-with-resources语句
try-with-resources 就是 一个声明了一个或做个资源的 try 块.任何 实现了 java.lang.AutoCloseable
接口且所包含的对象都实现了java.io.Closeable
的任何对象都能够被用作一个资源.
static String readFirstLineFromFile(String path) throws IOException {
// try 声明了一个 br.
// 从 jdk 1.7 开始,BufferedReader实现了 java.lang.AutoCloseable 接口
try (BufferedReader br =
new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
声明多个资源的try:
try (
java.util.zip.ZipFile zf =
new java.util.zip.ZipFile(zipFileName);
java.io.BufferedWriter writer =
java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {
// Enumerate each entry
for (java.util.Enumeration entries =
zf.entries(); entries.hasMoreElements();) {
// Get the entry name and write it to the output file
String newLine = System.getProperty("line.separator");
String zipEntryName =
((java.util.zip.ZipEntry)entries.nextElement()).getName() +
newLine;
writer.write(zipEntryName, 0, zipEntryName.length());
}
}
try-catch-finally 示例:
public void writeList() {
PrintWriter out = null;
try {
System.out.println("Entering" + " try statement");
out = new PrintWriter(new FileWriter("OutFile.txt"));
for (int i = 0; i < SIZE; i++) {
out.println("Value at: " + i + " = " + list.get(i));
}
} catch (IndexOutOfBoundsException e) {
System.err.println("Caught IndexOutOfBoundsException: "
+ e.getMessage());
} catch (IOException e) {
System.err.println("Caught IOException: " + e.getMessage());
} finally {
if (out != null) {
System.out.println("Closing PrintWriter");
out.close();
}
else {
System.out.println("PrintWriter not open");
}
}
}
如何抛出异常?
方法体内通过 throw
关键字抛出异常
方法签名处通过 throws
关键字抛出异常
public void writeList() throws IOException
异常类继承体系
异常类命名规则
命名以Exception
结尾.
I/O流表示输入源或输出目标。一个流可以表示许多不同类型的源和目的地,包括磁盘文件、设备、其他程序和内存数组。
Byte Streams字节流
程序使用字节流演示字节的输入和输出.
所有字节流都是InputStream
和 OutputStream
的后代.
以FileInputStream
文件字节流作为示例:
package com.black.basic.io.bytestreams;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 文件字节流演示类
*
* @author black
* @date 2022-01-19 10:21:032
*/
public class FileIOStreamDemo {
public static void main(String[] args) {
FileIOStreamDemo fileIOStream = new FileIOStreamDemo();
// 获取工程里文件
ClassLoader classLoader = fileIOStream.getClass().getClassLoader();
String file = classLoader.getResource("com/black/basic/io/test_file_en.txt").getFile();
// 打印文件内容
fileIOStream.printContent(file);
System.out.println("==========新增内容前===========");
// 追加一行内容
String pinkAge = "Pink 28\n";
fileIOStream.writeFile(file, pinkAge, true);
// 打印文件内容
System.out.println("==========新增内容后===========");
fileIOStream.printContent(file);
}
// 读取文件打印内容
public void printContent(String file) {
String content;
try {
content = this.readFile(file);
System.out.println("文件里的内容:");
System.out.print(content);
} catch (FileNotFoundException e) {
System.out.println(file + "文件不存在!");
} catch (IOException e) {
System.out.println("读取文件异常!,异常信息:" + e.getMessage());
}
}
// 读取文件
public String readFile(String fileName) throws FileNotFoundException, IOException {
StringBuilder content = new StringBuilder();
File file = new File(fileName);
// 如果文件不存在或不能写入,则不进行读取
if (!file.exists()) {
throw new FileNotFoundException(file + "文件不存在!");
}
if (!file.canRead()) {
throw new IOException(file + "文件无读取权限!");
}
// 从 InputStream 读取输入进来的字节
try (FileInputStream in = new FileInputStream(file)) {
// 字节
int b;
while ((b = in.read()) > 0) {
content.append((char) b);
}
}
// jdk 1.7 开始当 try 块退出时,会释放资源,此处不用再在finally中调用 in.close()关闭流释放资源
// 返回读取到的内容
return content.toString();
}
// 写入文件
public void writeFile(String fileName, String content, boolean append) {
File file = new File(fileName);
// 如果文件不存在或不能写入,则不进行读取
if (!file.exists()) {
System.out.println(file + "文件不存在!");
return;
}
if (!file.canWrite()) {
System.out.println(file + "文件无法写入!");
return;
}
try (FileOutputStream out = new FileOutputStream(file, append)) {
out.write(content.getBytes());
out.flush();
} catch (FileNotFoundException e) {
System.out.println(file + "文件不存在!");
} catch (IOException e) {
System.out.println("写入文件异常!,异常信息:" + e.getMessage());
}
}
}
test_file_en.txt 在 com.black.basic.io
包中, 文件内容:
# test file encoding UTF-8
name age
测试结果:
文件里的内容:
# test file encoding UTF-8
name age
==========新增内容前===========
==========新增内容后===========
文件里的内容:
# test file encoding UTF-8
name age
Pink 28
注意: 每次使用完流后必须关闭流,最好是在finally块中关闭.
Character Streams字符流
Java 平台使用 Unicode 规范存储字符.
所有字符流都是 Reader
和Writer
类的后代.
这里使用 FileReader
和 FileWriter
作为示例:
package com.black.basic.io.bytestreams;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* 文件字符流演示类
*
* @author black
* @date 2022-01-19 10:21:032
*/
public class FileIOCharacterStreamDemo {
public static void main(String[] args) {
FileIOCharacterStreamDemo fileIOStream = new FileIOCharacterStreamDemo();
// 获取工程里文件
ClassLoader classLoader = fileIOStream.getClass().getClassLoader();
String file = classLoader.getResource("com/black/basic/io/test_file_en.txt").getFile();
// 打印文件内容
fileIOStream.printContent(file);
System.out.println("==========新增内容前===========");
// 追加一行内容
String pinkAge = "Pink 28\n";
fileIOStream.writeFile(file, pinkAge, true);
// 打印文件内容
System.out.println("==========新增内容后===========");
fileIOStream.printContent(file);
}
// 读取文件打印内容
public void printContent(String file) {
String content;
try {
content = this.readFile(file);
System.out.println("文件里的内容:");
System.out.print(content);
} catch (FileNotFoundException e) {
System.out.println(file + "文件不存在!");
} catch (IOException e) {
System.out.println("读取文件异常!,异常信息:" + e.getMessage());
}
}
// 读取文件
public String readFile(String fileName) throws FileNotFoundException, IOException {
StringBuilder content = new StringBuilder();
File file = new File(fileName);
// 如果文件不存在或不能写入,则不进行读取
if (!file.exists()) {
throw new FileNotFoundException(file + "文件不存在!");
}
if (!file.canRead()) {
throw new IOException(file + "文件无读取权限!");
}
// 从 FileReader 读取字符,通过父类InputStreamReader与字节流转换
try (FileReader reader = new FileReader(file)) {
// 字符
int c;
// 每次读取一个字符
while ((c = reader.read()) > 0) {
content.append((char) c);
}
}
// jdk 1.7 开始当 try 块退出时,会释放资源,此处不用再在finally中调用 in.close()关闭流释放资源
// 返回读取到的内容
return content.toString();
}
// 写入文件
public void writeFile(String fileName, String content, boolean append) {
File file = new File(fileName);
// 如果文件不存在或不能写入,则不进行读取
if (!file.exists()) {
System.out.println(file + "文件不存在!");
return;
}
if (!file.canWrite()) {
System.out.println(file + "文件无法写入!");
return;
}
try (FileWriter writer = new FileWriter(file, append)) {
writer.write(content);
writer.flush();
} catch (FileNotFoundException e) {
System.out.println(file + "文件不存在!");
} catch (IOException e) {
System.out.println("写入文件异常!,异常信息:" + e.getMessage());
}
}
}
面向行操作的流:BufferedReader
,BufferedWriter
:
package com.black.basic.io.bytestreams;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* BufferedReader 示例
* @author black
* @date 2022-01-19 11:41:023
*/
public class FileIOBufferedReadDemo {
public static void main(String[] args) {
FileIOBufferedReadDemo fileIOStream = new FileIOBufferedReadDemo();
// 获取工程里文件
ClassLoader classLoader = fileIOStream.getClass().getClassLoader();
String file = classLoader.getResource("com/black/basic/io/test_file_en.txt").getFile();
// 打印文件内容
fileIOStream.printContent(file);
System.out.println("==========新增内容前===========");
// 追加一行内容
String pinkAge = "Pink 28\n";
fileIOStream.writeFile(file, pinkAge, true);
// 打印文件内容
System.out.println("==========新增内容后===========");
fileIOStream.printContent(file);
}
// 读取文件打印内容
public void printContent(String file) {
String content;
try {
content = this.readFile(file);
System.out.println("文件里的内容:");
System.out.print(content);
} catch (FileNotFoundException e) {
System.out.println(file + "文件不存在!");
} catch (IOException e) {
System.out.println("读取文件异常!,异常信息:" + e.getMessage());
}
}
// 读取文件
public String readFile(String fileName) throws FileNotFoundException, IOException {
StringBuilder content = new StringBuilder();
File file = new File(fileName);
// 如果文件不存在或不能写入,则不进行读取
if (!file.exists()) {
throw new FileNotFoundException(file + "文件不存在!");
}
if (!file.canRead()) {
throw new IOException(file + "文件无读取权限!");
}
// 从 BufferedReader 读取字符
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
// 行
String line;
// 每次读取一行
while ((line = reader.readLine()) != null) {
content.append(line);
// readline会去除回车换行,所以这里手动给拼接上
content.append("\n");
}
}
// jdk 1.7 开始当 try 块退出时,会释放资源,此处不用再在finally中调用 in.close()关闭流释放资源
// 返回读取到的内容
return content.toString();
}
// 写入文件
public void writeFile(String fileName, String content, boolean append) {
File file = new File(fileName);
// 如果文件不存在或不能写入,则不进行读取
if (!file.exists()) {
System.out.println(file + "文件不存在!");
return;
}
if (!file.canWrite()) {
System.out.println(file + "文件无法写入!");
return;
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, append))) {
writer.write(content);
writer.flush();
} catch (FileNotFoundException e) {
System.out.println(file + "文件不存在!");
} catch (IOException e) {
System.out.println("写入文件异常!,异常信息:" + e.getMessage());
}
}
}
Scanner 扫描
Scanner 可以扫描输入
从命令行读取IO
两种方式:
Console 使用参见下面代码:
import java.io.Console;
import java.util.Arrays;
import java.io.IOException;
public class Password {
public static void main (String args[]) throws IOException {
Console c = System.console();
if (c == null) {
// 在Eclipse 运行,一定会是null,使用CMD窗口运行则不会返回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.
}
}
Data streams数据流
数据流支持基本类型和String类型的二进制IO.所有数据流要么实现了 DataInput接口要么实现 DataOutput接口.
in = new DataInputStream(new
BufferedInputStream(new FileInputStream(dataFile)));
double price;
int unit;
String desc;
double total = 0.0;
try {
while (true) {
price = in.readDouble();
unit = in.readInt();
desc = in.readUTF();
System.out.format("You ordered %d" + " units of %s at $%.2f%n",
unit, desc, price);
total += unit * price;
}
} catch (EOFException e) {
// 内容读取结束
}
数据流通过捕获 EOFException 异常判断文件内容读取完毕.
Object Streams对象流
实现了Serializable接口的对象,可进行序列化.对象流的类: ObjectInputStream 和 ObjectOutputStream.
多个被引用对象的I/O,如下图:
java.nio.file 包和 java.nio.file.attr 包提供了对文件IO的支持. Path类是学习文件 IO的入口.
什么是 Path?
Path 就是 用/
拼接的目录.
目录结构图:
符号连接:
从 Java SE 7 开始引入 Path 类, Path 是 java.nio.file 包的主要入口之一.
创建一个 路径:
Path p1 = Paths.get("/tmp/foo");
Path p2 = Paths.get(args[0]);
Path p3 = Paths.get(URI.create("file:///Users/joe/FileTest.java"));
检索路径下的信息:
// Solaris syntax
Path path = Paths.get("/home/joe/foo");
System.out.format("toString: %s%n", path.toString());
System.out.format("getFileName: %s%n", path.getFileName());
System.out.format("getName(0): %s%n", path.getName(0));
System.out.format("getNameCount: %d%n", path.getNameCount());
System.out.format("subpath(0,2): %s%n", path.subpath(0,2));
System.out.format("getParent: %s%n", path.getParent());
System.out.format("getRoot: %s%n", path.getRoot());
结合两个路径:
Path p1 = Paths.get("/home/joe/foo");
// Result is /home/joe/foo/bar
System.out.format("%s%n", p1.resolve("bar"));
// Result is /home/joe
Paths.get("foo").resolve("/home/joe");
获取两个路径的相对路径:
Path p1 = Paths.get("home");
Path p3 = Paths.get("home/sally/bar");
// Result is sally/bar
Path p1_to_p3 = p1.relativize(p3); // p1 打开 sally/bar 可切换到 p3路径
// Result is ../..
Path p3_to_p1 = p3.relativize(p1);// p3 打开 ../.. 可切换到 p1路径
比较两个路径:
Path path = ...;
Path otherPath = ...;
Path beginning = Paths.get("/home");
Path ending = Paths.get("foo");
if (path.equals(otherPath)) {
// equality logic here
} else if (path.startsWith(beginning)) {
// path begins with "/home"
} else if (path.endsWith(ending)) {
// path ends with "foo"
}
java.io.File
和 java.nio.file
API对应关系:
并发编程中,2个基本执行单元:进程和线程
进程
进程拥有自己的运行环境,一个进程通常有一个完整的、私有的基本运行时资源集;特别是,每个进程都有自己的内存空间。
一个应用可能是由多个进程组成,进程之间需要通过 Inter Process Communication (IPC) 资源通信,比如 socket ,pipe等.
大多数 JVM 的实现运行在一个单进程中.Java 可以通过 ProcessBuilder 对象创建新的进程.
线程
线程有时被称为轻量级进程,进程和线程都提供一个执行环境,但是线程花费的资源更小.
每个进程最少有1个线程.线程共享进程的资源,比如进程的内存和打开的文件.
Java 平台多线程执行是个至关重要的特性.
启动应用程序的线程被称为主线程,负责创建额外的其他线程.
Thread
每个线程都与Thread类的实例相关联.
定义并启动一个线程
定义线程:1. 实例化 Thread 类 2. 实例化 Thread类的子类(后代)
启动线程: 调用 start 方法
new Thread().start();
使用线程
一个 Thread 对象对应一个线程,当调用 start() 方法就会启动一个新的线程,去执行这个Thread对象的run()方法.
注意: 只有调用 start() 方法才会生成1个新的线程.
Thread 对象的run方法是来自于 Runable接口.
run 方法源码:
public
class Thread implements Runnable {
...
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
...
@Override
public void run() {
// target 是 Runnable类型,
if (target != null) {
target.run();
}
}
...
}
从以上代码可以看出, 启动一个线程后,线程必须去执行Runable接口的run()方法. Thread 类本身也是Runable.
当我们在使用线程时,其实就是在 run 方法里添加代码.
使用线程的几种方式:
实现 Runable 接口编写run方法; 将此对象通过传给 Thread 对象或者传给线程池执行.
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
继承 Thread 类,重写 Thread类的run方法.
public class HelloThread extends Thread {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new HelloThread()).start();
}
}
实现 Callable 接口,编写call() 方法,将此对象传给线程池执行.
package com.black.basic.io.bytestreams;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
callable 接口示例
@author lihw
@date 2022-01-24 10:33:018
*/
public class CallableTaskDemo implements Callable
@Override
public String call() throws Exception {
// 重写 call 方法,添加任务处理逻辑,处理完成,返回成功
System.out.println("\tcallable task runing … ");
TimeUnit.SECONDS.sleep(3);
return "\tsuccess";
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 声明一个有2个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
// 声明并实例化一个 callable 任务
CallableTaskDemo task = new CallableTaskDemo();
// 将任务提交给线程池执行
System.out.println("开始执行任务…");
Future
// 同步等待任务执行后结果
System.out.println("\ttask result : " + result.get());
// 关闭线程池
System.out.println("开始关闭线程池… ");
executor.shutdown();
// 循环等待线程池关闭
while (true) {
if (executor.isTerminated()) {
System.out.println("线程池已关闭 ");
break;
}
}
}
}
sleep 休眠
使当前线程休眠,休眠状态可被中断.
// Thread.sleep休眠3秒
Thread.sleep(1000L);
// 使用 TimeUnit 休眠3秒
TimeUnit.SECONDS.sleep(3);
中断
中断就是告诉一个线程应该停止的指令.线程具体如何响应中断是由程序员决定的.
问:如何中断一个线程?
答: 调用 t.interrupt() 方法中断线程t.
问:如何探测到线程被中断了?
答:
1. Thread.currentThread().isInterrupted() 方法可判断线程是否被中断(不清除中断标志)
2. Thread.currentThread().interrupted() 方法可判断线程是否被中断过(清除中断标志,该方法调用后,isInterrupted() 返回false)
3. 通过捕获 InterruptedException 异常(比如 sleep方法,join 方法都会抛出此异常 )
测试代码:
package com.black.basic.io.bytestreams;
import java.util.concurrent.TimeUnit;
/**
* 中断示例
*
* @author lihw
* @date 2022-01-24 10:57:035
*/
public class InterruptDemo {
public static void main(String[] args) {
// 测试 Thread.currentThread().isInterrupted()
isInterrupted();
// 测试 Thread.currentThread().interrupted()
// interrupted();
}
public static void isInterrupted() {
// 定义任务
Thread t1 = new Thread(() -> {
while (true) {
System.out.println(Thread.currentThread().getName() + "任务执行中...");
// 判断执行此任务的线程是否被中断(isInterrupted() 不会清除中断标志)
if (Thread.currentThread().isInterrupted()) {
System.out.println("任务被中断");
System.out.println("当前线程" + Thread.currentThread().getName() + "中断状态:"
+ Thread.currentThread().isInterrupted());
// 探测到该线程被中断过,那么需要对中断行为进行一个响应,我这里是终止循环;如果这里终止循环,那么代码仍会继续执行
break;
}
}
}, "t1");
// 开启一个新线程执行任务
t1.start();
try {
// 主线程等待 1 毫秒
TimeUnit.MICROSECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 主线程中断 t 线程
t1.interrupt();
}
// 测试
public static void interrupted() {
// 定义任务
Thread t2 = new Thread(() -> {
while (true) {
System.out.println(Thread.currentThread().getName() + "任务执行中...");
// 判断执行此任务的线程是否被中断过(interrupted() 会清除被中断标志;)
if (Thread.currentThread().interrupted()) {
System.out.println("任务被中断");
System.out.println("当前线程" + Thread.currentThread().getName() + "中断状态:"
+ Thread.currentThread().isInterrupted());
// 探测到该线程被中断过,那么需要对中断行为进行一个响应,我这里是终止循环;如果这里终止循环,那么代码仍会继续执行
break;
}
}
}, "t2");
// 开启一个新线程执行任务
t2.start();
try {
// 主线程等待 1 毫秒
TimeUnit.MICROSECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 主线程中断 t 线程
t2.interrupt();
}
}
join 方法
join 方法允许一个线程等待另一个线程执行完成.
// t 是一个已经在运行的线程对象,当前线程会等待 t 线程执行结束再继续执行.
t.join();
join 方法可以通过抛出 InterruptedException 异常来响应中断.
配置实用工具
配置实用程序描述了用于访问部署应用程序时或应用程序用户提供的配置数据的api。
java.util.Properties
管理属性.属性在应用中的生命周期:
. . .
// create and load default properties
Properties defaultProps = new Properties();
FileInputStream in = new FileInputStream("defaultProperties");
defaultProps.load(in);
in.close();
// create application properties with default
Properties applicationProps = new Properties(defaultProps);
// now load properties
// from last invocation
in = new FileInputStream("appProperties");
applicationProps.load(in);
in.close();
. . .
命令行参数
Java 程序启动类的 main 方法能够接收命令行参数:
public class EchoTest {
public static void main (String[] args) {
for (String s: args) {
System.out.println(s);
}
}
}
运行 EchoTest
java EchoTest Drink Hot Java
// 控制台输出
Drink
Hot
Java
环境变量
Java 通过 System.getenv
方法检索可用的环境变量.
import java.util.Map;
public class EnvMap {
public static void main (String[] args) {
Map
for (String envName : env.keySet()) {
System.out.format("%s=%s%n",
envName,
env.get(envName));
}
}
}
将环境变量传递给新的进程:
// 使用 ProcessBuilder对象创建新的进程,默认使用Java虚拟机的环境变量,可通过 ProcessBuilder.environment 方法修改新进程的环境变量
Preferences API 保存用户偏好信息
同一个程序在每次运行完后,可以通过Preferences来记录用户的偏好(参考博客:https://www.cnblogs.com/zhongshiqiang/p/5852125.html)
Jar 包中使用 manifest 描述压缩包里的内容
Java Web Start应用的配置包含在jnlp文件中.
Java插件applet的配置部分取决于将applet嵌入到web页面中的HTML标记。
java.util.ServiceLoader
提供了SPI扩展机制
系统工具
System
类提供了一些预定义的 IO对象,
一些重要的系统属性:
读取系统属性:
System.getProperty("path.separator");
设置系统属性:
FileInputStream propFile =
new FileInputStream( "myProperties.txt");
Properties p =
new Properties(System.getProperties());
p.load(propFile);
System.setProperties(p);
系统安全
SecurityManager
是一个定义了应用程序的安全策略的对象.
获取 :
// 如果没有安全策略,则返回 null
SecurityManager appsm = System.getSecurityManager();
手机扫一扫
移动阅读更方便
你可能感兴趣的文章