Java基础复习笔记(注意点)

186 阅读8分钟

自动转换:将取值范围小的类型自动提升为取值范围大的类型

public static void main(String[] args) {
int i = 1;
byte b = 2;
// byte x = b + i; // 报错
//int类型和byte类型运算,结果是int类型
int j = b + i;
System.out.println(j);
}
byte、short、char‐‐>int‐‐>long‐‐>float‐‐>double

当一个 short 类型与 1 相加,会类型提升,但是还想给结果赋值给short类型变量,就需要强制转换。

public static void main(String[] args) {
short s = 1;
s = s + 1;//编译失败
s = (short)(s+1);//编译成功
}

JDK9的新特性JShell脚本工具

当编写的代码非常少的时候,而又不愿意编写类,main方法,也不愿意去编译和运 行,这个时候可以使用JShell工具。 启动JShell工具,在DOS命令行直接输入JShell命令。

+= 是一个运算符,只运算一次,并带有强制转换的特点,也就是说 s += 1 就是 s = (short)(s + 1)。

public static void main(String[] args){
            byte b1=1;
            byte b2=2;
            byte b3=1 + 2;
            byte b4=b1 + b2;
            System.out.println(b3);
            System.out.println(b4);
}

分析: b3 = 1 + 2 , 1 和 2 是常量,为固定不变的数据,在编译的时候(编译器javac),已经确定了 1+2 的结果并没有超过byte类型的取值范围,可以赋值给变量 b3 ,因此 b3=1 + 2 是正确的。

反之, b4 = b2 + b3 , b2 和 b3 是变量,变量的值是可能变化的,在编译的时候,编译器javac不确定b2+b3的结果是什么,因此会将结果以int类型进行处理,所以int类型不能赋值给byte类型,因此编译失败。

方法的参数为基本类型时,传递的是数据值. 方法的参数为引用类型时,传递的是地址值。

对象内存图

一个对象,调用一个方法内存图

两个对象,调用同一方法内存图

接口包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。

public interface InterFaceName {
    public abstract void method();//使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
    public default void method1() {
    // 执行语句
    }
    public static void method2() {
    // 执行语句
    }
    private void method3() {
    // 执行语句
    }
}

成员内部类 :定义在类中方法外的类。

外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
  • 内部类可以直接访问外部类的成员,包括私有成员。
  • 外部类要访问内部类的成员,必须要建立内部类的对象。
public class Person {
    private boolean live = true;
    class Heart {
        public void jump() {
            // 直接访问外部类成员
            if (live) {
            System.out.println("心脏在跳动");
            } else {
            System.out.println("心脏不跳了");
            }
        }
    }
    public boolean isLive() {
        return live;
    }
    public void setLive(boolean live) {
        this.live = live;
    }
    public static void main(String[] args) {
        Person p = new Person();
        Heart heart = p.new Heart();
        heart.jump();
        p.setLive(false);
    }
}

JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树。

Comparable和Comparator两个接口的区别

Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparator:强行对某个对象进行整体排序。可以将Comparator传递给sort方法(如Collections.sort或Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
public class Student implements Comparable<Student>{
    ....
    @Override
    public int compareTo(Student o) {
        return this.age‐o.age;//升序
    }
}
Collections.sort(list, new Comparator<Student>() {//独立的比较规则,不侵入类
    @Override
    public int compare(Student o1, Student o2) {
        // 年龄降序
        int result = o2.getAge()‐o1.getAge();//年龄降序
        if(result==0){//第一个规则判断完了 下一个规则 姓名的首字母 升序
            result = o1.getName().charAt(0)‐o2.getName().charAt(0);
        }
        return result;
    }
});

Java 9,添加了几种集合工厂方法,更方便创建少量元素的集合、map实例。新的List、Set、Map的静态工厂方法可以更方便地创建集合的不可变实例。

public class HelloJDK9 {
    public static void main(String[] args) {
        Set<String> str1=Set.of("a","b","c");
        //str1.add("c");这里编译的时候不会错,但是执行的时候会报错,因为是不可变的集合
        System.out.println(str1);
        Map<String,Integer> str2=Map.of("a",1,"b",2);
        System.out.println(str2);
        List<String> str3=List.of("a","b");
        System.out.println(str3);
    }
}
1:of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如
HashSet,ArrayList等待;
2:返回的集合是不可变的;

线程状态图

Lambda表达式的标准格式为:

(参数类型 参数名称) ‐> { 代码语句 }
  • 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
  • -> 是新引入的语法格式,代表指向动作。
  • 大括号内的语法与传统方法体要求基本一致。
public interface Calculator {
    int calc(int a, int b);
}
public class InvokeCalc {
    public static void main(String[] args) {
        invokeCalc(120, 130, (int a, int b) ‐> {
            return a + b;
        });
    }
    private static void invokeCalc(int a, int b, Calculator calculator) {
        int result = calculator.calc(a, b);
        System.out.println("结果是:" + result);
    }
}

省略规则
1. 小括号内参数的类型可以省略;
2. 如果小括号内有且仅有一个参,则小括号可以省略;
3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
public static void main(String[] args) {
    invokeCalc(120, 130, (a, b) ‐> a + b);
}

1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一
时,才可以使用Lambda。
2. 使用Lambda必须具有上下文推断。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

java.io.FileFilter 是一个接口,是File的过滤器。 该接口的对象可以传递给File类的 listFiles(FileFilter)作为参数, 接口中只有一个方法。

public class DiGuiDemo4 {
    public static void main(String[] args) {
        File dir = new File("D:\\aaa");
        printDir2(dir);
    }
    public static void printDir2(File dir) {
        // 匿名内部类方式,创建过滤器子类对象
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".java")||pathname.isDirectory();
            }
        });
    // 循环打印
        for (File file : files) {
            if (file.isFile()) {
             System.out.println("文件名:" + file.getAbsolutePath());
            } else {
                printDir2(file);
            }
        }
    }
}

// lambda的改写
File[] files = dir.listFiles(f ‐>{return f.getName().endsWith(".java") || f.isDirectory();});

JDK9中 try-with-resource 的改进,对于引入对象的方式,支持的更加简洁。被引入的对象,同样可以自动关闭,无需手动close,了解一下格式。

public static void main(String[] args) throws IOException {
    // 创建流对象
    final FileReader fr = new FileReader("in.txt");
    FileWriter fw = new FileWriter("out.txt");
    // 引入到try中
    try (fr; fw) {   //自动关闭
        // 定义变量
        int b;
        // 读取数据
        while ((b = fr.read())!=‐1) {
            // 写出数据
            fw.write(b);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

IO流

InputStream 、OutputStream

FileInputStream 、FileOutputStream

FileWriter 、FileReader

BufferedInputStream 、 BufferedOutputStream

BufferedReader、BufferedWriter

InputStreamReader、OutputStreamWriter

ObjectOutputStream、ObjectInputStream

PrintStream


Stream

public class StreamFilter {
public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("张无忌");
    list.add("周芷若");
    list.add("赵敏");
    list.add("张强");
    list.add("张三丰");
    list.stream()
    .filter(s ‐> s.startsWith("张"))
    .filter(s ‐> s.length() == 3)
    .forEach(System.out::println);   //s->System.out.println(s);
    }
}

Lambda表达式: n -> Math.abs(n)
方法引用: Math::abs

获取流、过滤姓张、过滤长度为3、逐一打印。
  • 所有的 Collection 集合都可以通过 stream 默认方法获取流;
  • Stream 接口的静态方法 of 可以获取数组对应的流。
Map<String, String> map = new HashMap<>();
// ...
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
Stream<String> stream = Stream.of(array);

常用方法

逐一处理:forEach

void forEach(Consumer<? super T> action);
public class Demo12StreamForEach {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");
        stream.forEach(name‐> System.out.println(name));
    }
}
java.util.function.Consumer<T>接口是一个消费型接口。
Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。

过滤:filter

可以通过 filter 方法将一个流转换成另一个子集流。
Stream<T> filter(Predicate<? super T> predicate);
 java.util.stream.Predicate 函数式接口,其中唯一的抽象方法为:
 boolean test(T t);
 该方法将会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的 filter 方法将会留用元素;如果结果为false,那么 filter 方法将会舍弃元素。
public class StreamFilter {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.filter(s ‐> s.startsWith("张"));
    }
}

映射:map

如果需要将流中的元素映射到另一个流中,可以使用 map 方法。方法签名:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
 java.util.stream.Function 函数式接口,其中唯一的抽象方法为:
 R apply(T t);
public class StreamMap {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("10", "12", "18");
        Stream<Integer> result = original.map(str‐>Integer.parseInt(str));
    }
}