对于在作用域里定义的变量,作用域同时决定了它的“可见性”以及“存在时间”。在JAVA里,作用域是由花括号的位置决定的。JAVA用一对大括号作为语句块的范围,称为作用域,在作用域里定义的变量,只有在该作用域结束之前才可使用。
在JAVA中,变量的作用域分为四个级别:类级、对象实例级、方法级、块级。
* 类级变量又称全局级变量或静态变量,需要使用static关键字修饰。类级变量在类定义后就已经存在,占用内存空间,可以通过类名来访问,不需要实例化。
* 对象实例级变量就是成员变量,实例化后才会分配内存空间,才能访问。成员变量是定义在方法之外,类之内的。成员变量随着对象的创建而存在,随着对象的消失而消失。
* 方法级变量就是在方法内部定义的变量,就是局部变量。局部变量在调用了对应的方法时执行到了创建该变量的语句时存在,局部变量的作用域从它被声明的点开始,一旦出了自己的作用域马上从内存中消失。
* 块级变量就是定义在一个块内部的变量,变量的生存周期就是这个块,出了这个块就消失了,比如 if、for 语句的块。块是指由大括号包围的代码
* 方法内部除了能访问方法级的变量,还可以访问类级和实例级的变量。
* 块内部能够访问类级、实例级变量,如果块被包含在方法内部,它还可以访问方法级的变量。
* 类级变量和成员变量是有默认的初始值。
虽然JAVA会自动初始化成员变量,但自动初始化会带来一些错误,所以在使用变量之前最好对其进行初始化,以保证变量的使用符合自己想要的效果;默认初始化只对于JAVA成员变量有效,如果要使用局部变量必须要对其进行初始化,否则会得到编译错误。
数据类型 默认的初始值
int 0
float 0.0f
double 0.0
boolean false
char ' '
引用数据类型 null
* 方法级和块级的变量没有默认的初始值,必须被显示地初始化,否则不能访问
* 同一作用域范围的包裹下成员变量名和局部变量名是可以变量名相同的。在方法中使用变量时,如果不指明使用成员变量还是局部变量,那么默认使用局部变量(就近原则),但是如果局部变量超出了它本身的作用域范围则会失效,被JVM垃圾回收,那么则可以重复命名此变量。
* 同一个作用域范围的包裹下局部变量和局部变量不可以变量名相同(作用域内不能重复命名)。
* lambda 表达式只能引用final 类型的外层局部变量,就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。与匿名函数同理
lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
举例: intnum=1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
//报错信息:Local variable num defined in an enclosing scope must be final or effectively final
//在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
String first = "";
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错
int num = 1;
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;
//报错信息:Local variable num defined in an enclosing scope must be final or effectively final
//在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
String first = "";
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错
方法引用
若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”,可以理解为方法引用是lambda表达式的另外一种表达形式
主要有三种语法格式:
- 对象 :: 实例方法名
- 类 :: 静态方法名
- 类 :: 实例方法名
被引用的方法的参数和返回值必须和要实现的抽象方法的参数和返回值一致
静态方法引用
//格式:Classname :: staticMethodName 和静态方法调用相比,只是把 . 换为 ::
String::valueOf 等价于lambda表达式 (s) -> String.valueOf(s)
Math::pow 等价于lambda表达式 (x, y) -> Math.pow(x, y);
实例对象方法引用
//格式:instanceReference::methodName
class ComparisonProvider{
public int compareByName(Person a, Person b){
return a.getName().compareTo(b.getName());
}
public int compareByAge(Person a, Person b){
return a.getBirthday().compareTo(b.getBirthday());
}
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
超类上的实例方法引用
//格式:super::methodName
//还可以使用this
泛型类和泛型方法引用
public interface MyFunc<T> {
int func(T[] als, T v);
}
public class MyArrayOps {
public static <T> int countMatching(T[] vals, T v) {
int count = 0;
for (int i = 0; i < vals.length; i++) {
if (vals[i] == v) count++;
}
return count;
}
}
public class GenericMethodRefDemo {
public static <T> int myOp(MyFunc<T> f, T[] vals, T v) {
return f.func(vals, v);
}
public static void main(String[] args){
Integer[] vals = {1, 2, 3, 4, 2, 3, 4, 4, 5};
String[] strs = {"One", "Two", "Three", "Two"};
int count;
count=myOp(MyArrayOps::<Integer>countMatching, vals, 4);
System.out.println("vals contains "+count+" 4s");
count=myOp(MyArrayOps::<String>countMatching, strs, "Two");
System.out.println("strs contains "+count+" Twos");
}
}
构造器引用
通过函数式接口实例化类时可以用构造器引用,引用到的是方法参数个数和类型匹配的构造器
//格式:ClassName :: new,调用默认构造器。
//lambda方式
Supplier<Passenger> supplier1 = () -> new Passenger();
//构造器引用:通过类型推断,引用无参构造器
Supplier<Passenger> supplier2 = Passenger::new;
//lambda方式
BiFunction<String, String, Passenger> function1 = (x, y) -> new Passenger(x, y);
//构造器引用:通过类型推断,引用有两个String参数的构造器
BiFunction<String, String, Passenger> function2 = Passenger::new;
数组引用
//lambda方式
Function<Integer, String[]> fun1 = (x) -> new String[x];
String[] strs1 = fun1.apply(10);
//数组引用
Function<Integer, String[]> fun2 = String[]::new;
String[] strs2 = fun2.apply(10);
Stream
A sequence of elements supporting sequential and parallel aggregate operations.
1、Stream是元素的集合,这点让Stream看起来用些类似Iterator;
2、可以支持顺序和并行的对原Stream进行汇聚的操作;
特性:
- 不存储数据
- 不改变源数据
- 延迟执行
使用步骤:
- 创建Stream数据源;
- 数据处理,转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换);
- 对Stream进行聚合(Reduce)操作,获取想要的结果;
创建数据源:
1、Collection.stream(); 从集合获取流。
2、Collection.parallelStream(); 从集合获取并行流。
3、Arrays.stream(T array) or Stream.of(); 从数组获取流。
4、BufferedReader.lines(); 从输入流中获取流。
5、IntStream.of() ; 从静态方法中获取流。
6、Stream.generate(); 自己生成流
public void createStream() throws FileNotFoundException {
List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
String[] nameArr = {"Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter"};
// 集合获取 Stream 流
Stream<String> nameListStream = nameList.stream();
// 集合获取并行 Stream 流
Stream<String> nameListStream2 = nameList.parallelStream();
// 数组获取 Stream 流
Stream<String> nameArrStream = Stream.of(nameArr);
// 数组获取 Stream 流
Stream<String> nameArrStream1 = Arrays.stream(nameArr);
// 文件流获取 Stream 流
BufferedReader bufferedReader = new BufferedReader(new FileReader("README.md"));
Stream<String> linesStream = bufferedReader.lines();
// 从静态方法获取流操作
IntStream rangeStream = IntStream.range(1, 10);
rangeStream.limit(10).forEach(num -> System.out.print(num+","));
System.out.println();
IntStream intStream = IntStream.of(1, 2, 3, 3, 4);
intStream.forEach(num -> System.out.print(num+","));
}
数据处理/转换
中间操作,可以有多个,返回的是一个新的stream对象,惰性计算,只有在开始收集结果时中间操作才会生效。
map (mapToInt, flatMap ):把对象映射成另一种对象
public void mapTest() {
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 映射成 2倍数字
List<Integer> collect = numberList.stream()
.map(number -> number * 2)
.collect(Collectors.toList());
collect.forEach(number -> System.out.print(number + ","));
System.out.println();
numberList.stream()
.map(number -> "数字 " + number + ",")
.forEach(number -> System.out.println(number));
}
public void flatMapTest() {
Stream<List<Integer>> inputStream = Stream.of(
Arrays.asList(1),
Arrays.asList(2, 3),
Arrays.asList(4, 5, 6)
);
List<Integer> collect = inputStream
.flatMap((childList) -> childList.stream())
.collect(Collectors.toList());
collect.forEach(number -> System.out.print(number + ","));
}
// 输出结果
// 1,2,3,4,5,6,
filter:数据筛选,相当于if判断
public void filterTest() {
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> collect = numberList.stream()
.filter(number -> number % 2 == 0)
.collect(Collectors.toList());
collect.forEach(number -> System.out.print(number + ","));
}
distinct:去重
public void distinctTest() {
List<String> list = Arrays.asList("AA", "BB", "CC", "BB", "CC", "AA", "AA");
long l = list.stream().distinct().count();
System.out.println("count:"+l);
String output = list.stream().distinct().collect(Collectors.joining(","));
System.out.println(output);
}
sorted
peek
limit:获取前n个元素
skip:丢弃前n个元素
public void limitOrSkipTest() {
List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
ageList.stream()
.limit(3)
.forEach(age -> System.out.print(age+","));、//11,22,13
System.out.println();
ageList.stream()
.skip(3)
.forEach(age -> System.out.print(age+","));//14,25,26
}
parallel:并行流
public void parallelTest(){
Long resourse = LongStream.rangeClosed(0,1000000000L)
.parallel().reduce(0,Long::sum);
System.out.println(resourse);
}
sequential
unordered
聚合收集结果
stream处理的最后一步,执行完stream就被用尽了不能继续操作。
forEach:遍历stream,不能return/break,支持lambda
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numberList.stream().forEach(number -> System.out.println(number+","));
forEachOrdered
toArray
reduce:累加器
//reduce中返回的结果会作为下次累加器计算的第一个参数
Optional accResult = Stream.of(1, 2, 3, 4).reduce((acc, item) -> {
System.out.println("acc : " + acc);
acc += item;
System.out.println("item: " + item);
System.out.println("acc+ : " + acc);
System.out.println("--------");
return acc;
});
collect
min
max
count
anyMatch
allMatch
noneMatch
findFirst
findAny
iterator
Statistics:统计
public void mathTest() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
IntSummaryStatistics stats = list.stream().mapToInt(x -> x).summaryStatistics();
System.out.println("最小值:" + stats.getMin());
System.out.println("最大值:" + stats.getMax());
System.out.println("个数:" + stats.getCount());
System.out.println("和:" + stats.getSum());
System.out.println("平均数:" + stats.getAverage());
}
// 输出结果
// 最小值:1
// 最大值:6
// 个数:6
// 和:21
// 平均数:3.5
groupingBy:分组聚合,相当于mysql的group by
public void groupByTest() {
List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
Map<String, List<Integer>> ageGrouyByMap = ageList.stream()
.collect(Collectors.groupingBy(age -> String.valueOf(age / 10)));
ageGrouyByMap.forEach((k, v) -> {
System.out.println("年龄" + k + "0多岁的有:" + v);
});
}
// 输出结果
// 年龄10多岁的有:[11, 13, 14]
// 年龄20多岁的有:[22, 25, 26]
partitioningBy:按条件分组
public void partitioningByTest() {
List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
Map<Boolean, List<Integer>> ageMap = ageList.stream()
.collect(Collectors.partitioningBy(age -> age > 18));
System.out.println("未成年人:" + ageMap.get(false));
System.out.println("成年人:" + ageMap.get(true));
}
// 输出结果
// 未成年人:[11, 13, 14]
// 成年人:[22, 25, 26]
自己生成Stream
public void generateTest(){
// 生成自己的随机数流
Random random = new Random();
Stream<Integer> generateRandom = Stream.generate(random::nextInt);
generateRandom.limit(5).forEach(System.out::println);
// 生成自己的 UUID 流
Stream<UUID> generate = Stream.generate(UUID::randomUUID);
generate.limit(5).forEach(System.out::println);
}
//使用limit进行短路
short-circuiting
有一种 Stream 操作被称作 short-circuiting
,它是指当 Stream 流无限大但是需要返回的 Stream 流是有限的时候,而又希望它能在有限的时间内计算出结果,那么这个操作就被称为short-circuiting
。例如 findFirst
操作。
findFirst:找出stream中第一个元素
public void findFirstTest(){
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Optional<Integer> firstNumber = numberList.stream()
.findFirst();
System.out.println(firstNumber.orElse(-1));
}
//找出第一个元素后就会停止遍历,相当于短路操作
解决终端操作只能一个的问题
Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
streamSupplier.get().anyMatch(s -> true); // ok
streamSupplier.get().noneMatch(s -> true); // ok
并行迭代器
//tryAdvance 相当于普通迭代器iterator 串行处理
public void iterator(){
AtomicInteger num = new AtomicInteger(0);
while(true){
boolean flag = spliterator.tryAdvance((i) ->{
num.addAndGet((int)i);
System.out.println(i);
});
if(!flag){
break;
}
}
System.out.println(num);
}
//trySplit将list分段,每段单独处理,为并行提供可能
public void spliterator(){
AtomicInteger num = new AtomicInteger(0);
Spliterator s1 = spliterator.trySplit();
Spliterator s2 = spliterator.trySplit();
spliterator.forEachRemaining((i) ->{
num.addAndGet((int)i);
System.out.println("spliterator:"+i);
});
s1.forEachRemaining((i) ->{
num.addAndGet((int)i);
System.out.println("s1:"+i);
});
s2.forEachRemaining((i) ->{
num.addAndGet((int)i);
System.out.println("s2:"+i);
});
System.out.println("最终结果:"+num);
}
//利用分段,开启多线程处理
public void spliterator2() throws InterruptedException {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
run(spliterator.trySplit());
return "future1 finished!";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
run(spliterator.trySplit());
return "future2 finished!";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
run(spliterator);
return "future3 finished!";
});
CompletableFuture<Void> combindFuture = CompletableFuture.allOf(future1, future2);
try {
combindFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("future1: " + future1.isDone() + " future2: " + future2.isDone());
System.out.println("最终结果为:" + count);
}
public void run(Spliterator s1) {
final String threadName = Thread.currentThread().getName();
System.out.println("线程" + threadName + "开始运行-----");
s1.forEachRemaining(new Consumer() {
public void accept(Object o) {
count.addAndGet((Integer)o);
}
});
System.out.println("线程" + threadName + "运行结束-----");
}
HashMap
JDK8优化了HashMap的实现, 主要优化点包括:
- 将链表方式修改成链表或者红黑树的形式
- 修改resize的过程,解决JDK7在resize在并发场景下死锁的隐患
- JDK1.7存储使用Entry数组, JDK8使用Node或者TreeNode数组存储
当链表长度大于8是链表的存储结构会被修改成红黑树的形式。
查询效率从O(N)提升到O(logN)。链表长度小于6时,红黑树的方式退化成链表。
JDK7链表插入是从链表头部插入, 在resize的时候会将原来的链表逆序。
JDK8插入从链表尾部插入, 因此在resize的时候仍然保持原来的顺序。
其他
新增JVM工具:jdeps提供了用于分析类文件的命令行工具。
使用metaSpace代替永久区
新增NMT(Native Memeory Trace)本地内存跟踪器
日期和时间api:
老版本的Date类有两个,java.util.Date和java.sql.Date线程不安全