前一段时间有计划重看一下java基础的内容,只是各种事不断的打断,到现在过了一部分内容。刚开始有些基础的东西,把之前没有注意到的地方,进行一个简单的记录;
有些地方,还可以进一步深入,这里只做浅层的总览了,之后再针对性的深究,写点demo。
1.基本数据类型的默认值:
public class DefaultValue {
char a;
boolean b;
byte c;
short d;
int e;
long f;
float g;
double h;
public static void main(String[] args) {
DefaultValue defaultValue = new DefaultValue();
defaultValue.printJavaDataDefaultValue();
}
//打印java中8个基本数据类型的默认值
public void printJavaDataDefaultValue() {
System.out.println("**********打印java中8个基本数据类型的默认值**********");
System.out.println("char = " + a);
System.out.println("a=='\\u0000' = "+ (a=='\u0000'));
System.out.println("boolean = " + b);
System.out.println("byte = " + c);
System.out.println("short = " + d);
System.out.println("int = " + e);
System.out.println("long = " + f);
System.out.println("float = " + g);
System.out.println("double = " + h);
}
}
输出:
打印java中8个基本数据类型的默认值
char =
a==’\u0000’ = true
boolean = false
byte = 0
short = 0
int = 0
long = 0
float = 0.0
double = 0.0
其中 char 的默认值是’\u0000’
2.引用的数组
引用数组声明的时候,只申请了引用的空间,具体对象还需要再次实例化;
Cat[] cats=new Cat[10];
cats[0]=new Cat();
//cat[1]为默认值null
3.java方法传值、传引用
方法的入参对于基本数据类型和字符串常量来说,传递的其实只是这个值本身的一个拷贝。对于外面的变量来说,并没有改变什么,入参的作用域也仅限函数内部,属于局部变量(包括包装类型和String)
对于数组、引用则存在如果在方法内对引用指向的内存内容修改,则外部也修改的问题。因为传递的是引用的拷贝,在方法内,拿到引用地址之后,如果修改引用变量的指向,则外部不变;如果修改指向地址的内容,则外部改变。
public class TestCat {
public static void main(String[] args) {
Cat c=new Cat();
c.name="dudu";
test1(c);
System.out.println(c.name);
test2(c);
System.out.println(c.name);
}
public static void test1(Cat cat){
cat=new Cat();//相当于修改局部变量cat的引用指向,不会对外部c的内容修改
cat.name="test1";
}
public static void test2(Cat cat){
cat.name="test2";//相当于对用一个引用指向的内容作了修改
}
}
输出
dudu
test2
4.重载的参数匹配
对于一个普通的方法的调用,传参不一定要完全的类型匹配,实参可以自动类型转换成形参也是可以的。
比如一个方法fun(double price);
int a=10;
fun(a);
可以传递int类型的变量,自动类型转换。
当某个方法被重载,此时如果传参类型没有直接匹配项,重载会选择就“近”的进行调用。
当重载传参数引用,且引用类型没有完全匹配,则会根据继承关系,沿着参数当前类型,向下理。
顺带补充 重载的条件:
1.函数名相同
2.参数个数不同或者参数类型不同
3.函数重载和返回值类型无关
5.构造方法
构造方法,在java中被编译成<init>
方法,包括在类中,声明成员变量时,进行赋值,也会编译到该方法中。
然后是构造方法之间调用的一些注意事项
如下代码为例
public class Cat{
private String name;
private int age;
//构造方法无返回值
public Cat(String name){
this.name=name;
}
public Cat(int age){
this.age=age;
}
public Cat(int age,String name){
this(name);
//this(age); //调用重载的构造方法,必须是第一行,否则会报错。像这样,连着调用两个也不行。
//this.age="dudu";//在调用重载构造方法时,不可以使用未初始化的成员变量,
}
}
1.构造方法里才能调用重载的构造方法;形式是this(参数列表),但必须是方法的第一行;
2.构造方法不能调用自己,会死循环;
3.在调用重载的构造方法时,不可以使用成员变量。语义上讲,此时对象还没有初始化完成,处于中间状态;
4.构造方法无返回值。
6.静态方法
一些注意事项:
1.静态方法可以访问静态变量,包括自己类的静态变量,和可以访问的其他类的静态变量;
2.静态方法没有this自引用,不属于某个实例,调用的时候直接用类名调用,不能直接访问成员变量和成员方法;
但是可以通过传对象参数,或者新建对象,来获取成员变量。(根本原因:无自引用)
7.静态代码块
一些注意事项:
1.静态代码块中使用某个静态变量,必须在静态变量后面;(代码块中仅仅赋值是可以的 【少用】)
2.一个类中可以有多个静态代码块,但是,是有顺序的,相当于在编译的时候,会将多个代码块顺序组合在一起,组织到clinit(即class init)方法中。
8.Math类
1.一般情况下,调用的都是Math的静态方法,其构造函数是private。但是可以通过静态方法,来调用其构造函数来返回实例化对象。
2.Math.random(),实际是调用了Random类,实例化了对象,进行随机数生成。
Random类在java.util包中。在java.lang包中,不需要显示import,其他包需要显示导入。
Random中的nextInt(),返回值是int的整个值域,包括正负;
3.还有常用的Math.abs() ;Math.round()
BigInteger BigDecimal 大数 加减用大数类型封装好的函数 如add()
9.String类
1.String对象是不可变的,因为其存储字符的数据被private修饰,且不提供任何修改方法,所以一旦生成,内容不可修改。
2.虽然不是基本数据类型,但是允许直接=“dudu”,来进行对象的创建。可以不用使用new String(“dudu”)
3.toUpperCase() toLowerCase() 返回值是来修改字母大小写,但对象本身不变,是创建了新的对象。
4.charAt()
substring() 单参数是从哪里开始到最后; 双参数是从截取的下标,end不取。
toCharArray() 得到字符数组
split() 根据字符或者字符串进行分割
indexOf() 某个字符第一次出现的位置
lastIndexOf() 从最后找 第一次出现的位置
contains() 是否包括某子字符串
equals()
equalsIgnoreCase() 比较是否相等 忽略大小写
trim() 去掉字符串中的空格
10.StringBuilder
在java.lang包中
append(); 返回了自引用,所以可以直接多个拼接 str.append(" “).append(”–")
reverse();操作原对象
toString();返回新string对象
delete(a,b); a b是下标,取a不取b;
insert(a,"–"); 从下标a处开始插入;
11.重写
1.通过使用和父类方法签名一样,且返回值也完全一样的(可以自动转化的类型也不可以)方法,让子类覆盖掉(override)父类的方法。
2.覆盖的时候,不可以让其可见性更低,但是可以用更高的修饰符。
因为父类引用指向子类对象时,调用其重写的方法,如果此时为private,不符合只有自己调用的原则。
12.super
1.属于子类和父类交流的桥梁,通过super可以调用父类中的public方法,但并不是父类的引用,(不是一个引用),和this自引用不一样,不可以返回。
2.使用super调用父类的构造方法,必须是子类构造方法的第一个语句;
可以使用表达式;
不可以使用super访问父类的属性和方法,不可以使用子类的成员变量和方法,可以使用静态变量和方法。
3.如果父类没有无参的构造方法,那么子类需要在构造方法第一行,通过super调用一个父类的有参数的构造方法。
13.父类子类引用
1.父类引用可以指向子类对象,但是决定可以调用哪些方法的是引用的类型。
即父类引用只能调用父类中有的方法,子类特有的方法无法调用,即便实际指向的是子类对象。
能调用哪些方法是引用类型决定,调用哪个方法是引用所指向的对象决定
换言之,如果子类没有覆盖某个方法,那么就回去父类中找,如果还没有就回去父类的父类去找。既然引用指向成立,那么必定是有一个父类兜底的。
2.如果确定一个父类引用指向的是子类对象,那么可以强制类型赋值给子类引用。
但是如果指向不符合要求,那么强制类型转化失败。
14.instanceof
一个操作符,用于判断一个引用指向的对象是否是一个类型或者其子类,是则返回true,不是返回false;null一定返回false。
多用于强制类型转换之前判断是否可以转换。
Animal anm=new Cat();
if(anm instanceof(Cat)){
Cat c=(Cat) anm;
}
15.final
1.修饰类:不可继承
2.修饰方法:不可给覆盖
3.修饰成员变量:(一定要赋值,且赋值一次)只允许在生命时初始化或者构造方法中赋值,其他地方不允许赋值。
如果是静态变量:也一样,必须赋值一次,可以声明时赋值,也可以在静态代码块赋值。
4.不可以修改构造方法
5.修饰局部变量,也是一样,一定要赋值,且赋值一次。
6.修饰引用,则引用指向的对象不可修改,但是对象的内容可以修改
16.继承里的静态方法
1.静态方法是可以被继承的;要求和普通的覆盖相同。
2.用引用调用静态方法,没有覆盖。
对比:
非静态方法:能调用的方法取决于引用类型,具体调用哪个取决于指向的对象;
静态方法:能调用的,和具体调用的都是由引用类型决定的。
所以一般不用引用调用静态方法,都是使用类名调用。
追其根源是静态方法直接和类相关,没有this自引用。
17.hashCode和equals方法
1、hashCode相对标识对象之间不一样的int整数;
equals是判断两个对象在逻辑上是否相等;
都是Object类中有的方法,自己写的类要使用这两个方法的时候,一般都是要覆盖的。
2、缺省情况下,equals仅仅是比较两个对象的引用是否相等;
覆盖的原则是:如果equals是true,是hashCode相等的充分非必要条件。
3、编译器可以自动生成这个两个方法的覆盖:
equals会逐个比较成员变量是否相等。
hashCode会将成员变量综合起来,使用Object.hash()方法生成哈希码。
18.String判断相等的特殊情况
每一个字符串都是一个对象,且String对象是不可修改的,所以判断字符串是否相等,要使用euqals(),但是如下代码,输出都是true。
String str1="aabb";
String str2="aa"+"bb";
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
java对字符串优化,将字符串统一管理,如果创建的字符串不是很多,新创建一个字符串的时候,会去找是否有值一样的,因为String不可修改,所以就直接返回该引用。
如果字符串很长很长,突破java优化的限制, 那么就会创建两个对象。
总的来说,平常还是要使用equals()比较。
19.动态参数
必须是参数列表中的最后一个参数,类型后加…
fun(String… args){};
调用的时候,可以不传递参数,fun()等效于fun(new String[0])
也可以传递多个参数,fun(“aa”, “bb”,", “cc”)等效于fun(new String[]{“aa” ,“b”,",“cc”})
20.Class类
每个class类的实例,都代表一个类; 通过这个类的Class实例,可以获取一个类所有的信息,包括成员变量、方法等。
class类的实例可以通过object类中的getClass()方法得到,
拥有getName(), getSimpleName()两个基本方法,SimpleName返回省略了包名目录:
getField(String Name)方法,可以获得Field对象, 是对类中成员变量的描述,Field对象拥有多个函数,getName()、 getType()等等方法。getMethod()方法,可以获得Method对象, 是对方法的描述,可以获得参数、返回值。
例如: getMethod(“euqals” object.class),第一 个参数是方法名,第二个参数是动态参数,为方法参数的类型,可以是多个。
21.反射(一)
对属性进行获取的样例:
Cat cat=new Cat("dudu");
Class clazz=Cat.class;
Field nameField=clazz.getField("name");
//通过nameField可以查看某个对象该变量的值。这里查看cat对象的name属性。
System.out.println(nameField.get(cat));
//也可以set
nameField.set(cat,"haha");
//其实等效cat.name
//可以遍历所有变量
for(Field field:clazz.getFields()){
System.out.println(field.getType()+" "+field.getName());
}
dudu
class java.lang.String name
特别的,如果传递的是静态变量,那么对象传递是空就行
Field field=clazz.getField(“STATIC_MEMBER”);
System.out.println(nameField.get(null));
对方法获取的样例
Cat cat=new Cat("dudu");
Class clazz=Cat.class;
Method sayMethod=clazz.getMethod("say");
sayMethod.invoke(cat);
//调用有参数的静态方法getNameOf(Cat cat);
//第二个参数是变量类型。
Method staticMethod=clazz.getMethod("getNameOf",Cat.class);
String name=(String)staticMethod.invoke(null,cat);
System.out.println(name);
dudu:喵喵
dudu
invoke()第一个参数是对象,第二个参数是变量。返回值未知,需要强制类型转换。
22.反射(二)
对于类中private修饰的变量和方法,可以通过反射取到。
getDeclaredField()
getDeclaredMethod()
//name属性被private修饰
Field nameField=clazz.getDeclaredField("name");
nameField.setAccessible(true);
System.out.println(nameField.get(cat));
//也可以set
nameField.set(cat,"haha");
//say方法被private修饰
Method sayMethod=clazz.getDeclaredMethod("say");
sayMethod.setAccessible(true);
sayMethod.invoke(cat);
23.枚举
public enum TaskStatus{
//必须在开始的时候,以下列形式,创建所有的枚举对象
TODO(1),//中间是逗号
DONE(2);//最后一个是分号
//可以有属性
private int id;
//构造方法必须是private的,不写也是private;
private TaskStatus(int id){
this.id=id;
}
public int getId(){
return id;
}
}
枚举和类相比,特殊的地方,在于最开始的地方,调用枚举的构造方法,以创建所有的枚举对象。
构造方法是私有的,所以,该枚举类的实例是一开始创建好的,不许与私自在其他地方创建。
枚举类继承与enum,不允许再继承其他类。
enum类中有一些方法:
-
枚举类.values() 获得所有枚举的实例数组
-
ordinal() 返回某个实例在枚举类中定义的顺序(从0开始)
-
name() 返回某个实例在枚举类中定义的名字。(大小写敏感)
-
枚举类.valueOf(“TODO”) 根据输入获得枚举对象
获取枚举实例的方法
- 枚举类.枚举名 TaskStatus.TODO
- 枚举类.valueOf(“TODO”) 根据输入获得枚举对象
因为枚举实例只能创建一次,所以两个枚举的引用可以直接使用==进行比较,看是否指向统一引用,判断是否一样。
24.接口相关
-
接口不能实例化对象,不能被类继承,要被类实现;接口之间可以多继承。
-
接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract
-
接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
-
JDK 1.8 以后,接口里可以有静态方法和方法体了。
重写接口中声明的方法时,需要注意以下规则:
-
类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
-
类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。 (当接口继承其他接口时,可以有重复的方法,但是签名一样的方法,则要求返回值必须一样,否则编译错误。)
-
如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
抽象类:
被abstract 方法修饰,可以有抽象方法,不能被实例化;
有方法的接口:
java8之后,允许有静态方法,私有方法,带有缺省实现的方法。
-
使用default修饰,可以有方法体;
此时,如果一个类实现多个接口,且都有同名,同返回值的缺省方法,那么报错
-
接口可以有私有方法(private),不需要用default修饰,接口里的私有方法,可以认为是代码直接插入到使用的地方。并且在私有方法中,可以调用接口中其他抽象方法。
public interface Animal{
//加default修饰
default void say(){
System.out.printlm(description()+"default say");
}
//抽象方法
String getName();
//接口中可以有私有方法,可以通过this 调用抽象方法。
private String description(){
return "这是"+this.getName();
}
}
代码中的this,不是接口本身的this,指向了实现了接口的实例,只能调接口中的方法。
- 静态方法:不需要default修饰,可以被实现接口的类继承。缺省是(public static)
特别的,对于实现有缺省方法的接口,面对每个缺省方法,一个类可以有三个选择:
-
直接继承使用;
-
重新声明此方法为abstract,这部分代码拒之门外,类需要为抽象类
从这个抽象类之后的继承、实现不可以再读到该缺省方法。
-
覆盖,重新实现
25.静态内部类&非公有类
-
在类中使用static修饰的类,具有类本身的所有特点,也可以再次套静态内部类(不推荐)。
-
也可以使用访问控制符修饰(public、private…),和静态变量一样,也是类的静态组成部分。
-
内部类、外部类之间都可以互相直接访问对方的私有属性、方法。
-
在外部访问时,需要加上外部类,例如:外部类名.静态内部类;
在外部的代码,就不再可以直接访问私有属性了。
静态内部类实现单例模式----Math类
Math.random();
public static double random(){
return RandomNumberGeneratorHolder.randomNumberGenerator.netDouble();
}
RandomNumberGeneratorHolder是一个Math类的静态内部类;
private static final class RandomNumberGeneratorHolder{
static final Random randomNumberGenerator = new Random();
}
内部类中只有一个初始化一次的静态变量;
这样一来,利用了java的类加载机制,静态内部类不会过早初始化,实现单例模式,只有在调用Random之后,才会初始化,且保证只会初始化一次。
非公有类:
-
在同一文件内,但是定义在类外。
主要区别就是不能访问类的private属性;
26.成员内部类
-
不可以包含任何静态成分:静态方法、静态变量、静态内部类;
-
可以有final static的基本数据类型的变量
-
成员内部类中有一个外部类的引用,访问外部的成员变零可以使用该引用。所以也可以访问外部的private属性。
静态内部类: Phone phone; phone.speed;
成员内部类:Phone.this.speed;
-
如果在外部想创建内部类的对象,写法比较特殊:
Phone phone = new Phone("一些初始化"); Phone.CPU cpu = phone.new CPU("default");
需要指明内部类的外部类的引用,这里后面的phone就是指定的外部类的引用。
27.局部内部类
-
方法内部的类,叫作局部内部类
-
不可以包含静态的成分
-
不可以有访问控制符,和成员变量一样,都是类的组成部分。
-
可以有static final修饰的基本数据类型变量
-
也有一个外部类的引用,故而,也可以访问外部类的private属性,
类名.this.属性/方法
-
除此之外,局部内部类,还可以访问方法的参数和局部变量,但是必须是实际final(声明之后,不可再赋值)。
28.匿名类
匿名类是用来创建接口或者抽象类的实例的;可以出现在任何有代码的地方(赋值、参数传值)。
格式:
实例名 = new 接口名/抽象类名(){
//类体。必须包含的对抽象方法的实现;
//此外可以有成员变量。
//特性和局部内部类类似,访问方法的参数和局部变量,需要使实际final,也可访问外部类私有属性。
}
实现的是抽象类时,如果构造函数有参数,可以再实现匿名内部类的时候正常传参。
如果实例赋值对象时static,那么对标静态内部类的特性。
(外部类.this.私有属性×)
29.异常
所有异常的父类:Throwable
异常的种类(Throwable的两个子类):Error:无法处理的 和Exception :可以处理的
exception分为 checked exception 和unchecked exception。前者必须使用try catch或者throws处理;
Error和RuntimeException是unchecked exception的父类,一般使用 RuntimeException。
不是继承上述两个类的异常,是checked exception。
异常比较重要的信息:类型、错误信息、出错时的调用栈;
自定义异常:封装一些父类的构造方法,也可以自定义一些属性方法;
catch一个没有抛出的checked exception,会报错,java明确认为这个类型的异常不会发生;
catch一个没有抛出的unchecked exception,不会报错;
throws一个没有抛出的checked或者 unchecked exception 都不会报错。
对异常捕获传递,本身是一个很耗资源的操作,调用栈、类型匹配等,所以对于异常处理不可以处理正常的业务。
尽量不要使用throw catch来进行业务操作的返回,类似返回值功能,catch到什么异常就作什么类型的业务操作。
try-catch-finally:
finally认为是在方法返回后,后面的方法前,会在return语句后执行的。无论在return结束还是异常结束,finally都会执行;
在finally内改变return的变量值,是无效的,因为程序认为;
finally内最好不要有return 会把前面的return全部都失效,且会打乱exception的传递。
自动回收资源的try语句
(简化资源相关的异常处理)
资源需要实现AutoCloseable接口
try( MyAutoCloseableResource res1 = new MyAutoCloseableResource("res1");
MyAutoCloseableResource res2 = new MyAutoCloseableResource("res2")
){
//do
}catch(Exception e){
//
}
常见的异常:
NPE(NullPointerException):空指针异常
IndexOutOfBoundsException : 索引相关的异常
ClassCastException : 一个类型的引用强转成另一个类型的引用,出现不匹配的属性时
ClassNotFoundException:找不到类 (checked)
IOException:IO相关的 (checked)
30. 泛型
约束控制的元素类型
在方法中定义泛型:Generic Methods
在类型中定义泛型:Generic Type
public class MyGenericClass<First,Second>{
private First first;
private Second second;
...
public <Another> Another getAnother(Object val){
return (Another)val;
}
}
作用:
告诉编译器帮我们检查类型是否匹配,是否一样;
在使用的地方,进行类型转换。
如果在使用泛型时,出现类型转换错误,报错报在调用方。
java里是类型擦除的,并没有记住类型是什么,只是在编译的时候进行检查,在用的时候进行类型转换。
再探:
泛型类型不可以调用方法,因为不知道是什么类型,如果需要使用某个类方法,则需要给定类型的范围。
public class MyGenericClass<MyType extends GrandParent>{
private MyType val;
...
public String getAnother(MyType myType){
return myType.getName();
}
}
如上,给定范围之后,可以调用GrandParent的getName()方法。
协变和逆变:
是针对引用类型的,可以用在返回值类型,参数类型,等引用类型上。
创建对象的时候,不可以使用协变和逆变。
协变
-
泛型类型不管继承关系,只管严格匹配:
B是A的子类,但是
List<B>
不是List<A>
的子类; -
使用协变完成上述需求,语法:
void extMethod(List<? extends Parent> extParam){ for( Parent parent: extParam){ } }
这样这个参数可以接受List引用的泛型类型为Parent或者其子类。
-
也可以创建协变的引用,但是在创建对象时,不可以使用。
List<? extends Parent> list=new ArrayList<>();
但是使用这个带协变范围的引用,无法让具体的类型满足其参数要求,
因为这样的引用,可以直接指向类似
List<Child>
对象,这样一来,使用这个引用可以add(new Parent()); 但是这个对象实际是一个List<Child>
,就会报错。
逆变:
与协变相反,允许的类型为Parent及其父类;
限制同协变,只是针对引用上,不能在创建对象的时候使用。
使用这个带协变范围的引用,无法让具体的类型满足其参数要求,
语法:
List<? super Parent> list= null;
list= new List<? super Parent>() ×
写入时使用逆变;读取时使用协变。
31.Iterator
Iterable接口,主要功能就是返回一个Iterator<>对象,返回这样一个迭代器进行遍历。
实现Iterable接口,就可以支持forEach。
32.注解 annotation
@Override 重写
@Deprecated 过时
自定义注解
// 注解可以被用在哪个/哪些元素上,多个元素时使用{}
@Target(ElementType.METHOD)
// 注解会被留存到哪个阶段
@Retention(RetentionPolicy.RUNTIME)
//以上是每个注解都必须有的,不显示定义的话,会使用缺省值
// 定义一个自己的注解,需要@interface,实际上这个接口会继承Annotation接口
public @interface PrimaryProperty{
//支持基本数据类型,Class,String,枚举,其他注解 以及前面类型的数组
//可以指定缺省值
String defaultValue() default "N/A";
Class targetClass();
int abc();
Override is() default @Override;
}
//使用的时候--实现增加元数据的效果,
//如果是RUNTIME类型,可以通过反射获得这些注解信息,作为相应功能的配置,实现相应的功能。
@PrimaryProperty(targetClass=Test.class,abc=21)
33.lambda
函数式编程,函数不必依附于某个类而存在,可以作为参数和返回值;
lambda表达式必须能够符合接口中定义的抽象方法,参数、返回值、异常都必须匹配;
(参数)->{代码块}
可以取代只有一个抽象方法的接口(不包括default)
使用List的forEach进行举例:
List的forEach方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
其中Consumer接口主要有一个accept函数:
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
匿名内部类的实现:
list.forEach(
new Consumer<String>(){
@Override
public void accept(String s){
//processString是外部类的静态方法;outside是外部类的变量
processString(s+outside);
}
}
);
lambda实现:
list.forEach(s->processString(s+outside));
//不使用外部变量,传递静态方法,可以使用:
list.forEach(类名::方法名);
//不使用外部变量,传递非静态方法,可以使用:
list.forEach(实例::方法名);
//不使用外部变量,如果知道参数类型,那么可以传递参数类型里的方法
list.forEach(String::toUpperCase);
流的使用:
list.stream().filter(s-> s.length() > 4).map(String::toUpperCase).forEach(System.out.println);
使用collector让数据重新生成一个list:
List<String> list2 = list.stream().filter(s-> s.length() > 4).map(String::toUpperCase).collect(Collector.tolist());
34.基本数据类型的自动装箱和拆箱
auto boxing, auto unboxing
//自动拆箱
Integer a = 123;
int b = a;
//自动封箱--put,get都会把1封装成实例
Map<Integer, String> map = new HashMap<>();
map.put(1,"壹");
map.get(1);
//对于允许为null的数据结构
map.put(null,"无");
map.get(null); //是被允许的
for(int key : ,map.keySet()){} // 自动拆箱出错
使用三目运算符时,如果出现类型不一致,java会使用自动拆装箱的机制, 只要一个运算中有不同的类型,涉及到类型转换,那么编译器会往下(基本类型)转型,再进行运算。
int a = 12;
Integer b = null;
Integer c = true ? b : a;
正常运算下来,c=b=null
,但是会出现NPE;
因为类型不一致,b出现了自动拆箱,null调用intValue(),出现NPE。