白马:https://www.itbaima.cn/document/jviyz2hsht9ete5k
JDK1.8安装:https://blog.csdn.net/weixin_44084189/article/details/98966787
笔记规定:非主线内容使用引用记录
面向对象基础 对象的创建过程?以MyClass obj = new MyClass(10, "Hello");
为例——
声明:MyClass obj
声明指向类的引用变量
实例化:new
关键字触发对象的实际创建过程,为新对象分配内存空间,并返回对该对象的引用
初始化:
默认初始化:对象创建后其成员变量被赋予默认值(数值类型为0或false,对象引用为null等),
显式初始化:(手动规定的默认值方法被执行,如String name = test();
)
构造代码块:构造代码块按照顺序被执行,形如不在任何方法下也不是静态的{}
构造方法:接着调用构造方法,有自定义构造方法就调用,否则调用默认无参构造方法
继承问题:类有继承时,会从父类逐级向下初始化,按照上述顺序执行,直到当前类的构造方法被执行完毕
对象就绪:整条语句执行完成,可以使用对象进行操作
注意 :静态成员(包括静态变量和静态初始化块)按照它们在源代码中的出现顺序进行初始化
静态变量与静态方法?
属于类,不属于对象,静态方法只能直接访问静态变量。
所有被标记为静态的内容,会在类刚加载的时候就分配,而不是在对象创建的时候分配,所以说静态内容一定会在第一个对象初始化之前完成加载。
在Java中使用一个类之前,JVM并不会在一开始就去加载它,而是在需要时才会去加载(优化)一般遇到以下情况时才会会加载类:
访问类的静态变量,或者为静态变量赋值
new 创建类的实例(隐式加载)
调用类的静态方法
子类初始化时
其他的情况会在讲到反射时介绍
面向对象高级 内部类 创建在内部的类,内部类可以通过外部类名.this
语法来显式地使用这个外部类引用。
成员内部类 在Java中,外部类能够访问内部类(无论是静态内部类还是非静态内部类)的成员变量,但需要通过内部类的一个实例来访问这些成员。不过,需要注意的是访问的方式取决于内部类的类型:
非静态内部类(成员内部类) :这类内部类与外部类的实例相关联,因此不能直接访问其成员,必须通过创建内部类的一个实例来访问其成员。
静态内部类 :如果内部类被声明为static
,则它可以包含静态成员,并且可以直接通过类名访问这些静态成员,不需要外部类的实例。
eg:非静态内部类,需要创建实例,注意Test构造方法需要new一个Inner对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package org.example;public class Test { private final String name; private final Inner inner; public Test (String name, String innerName) { this .name = name; this .inner = new Inner (innerName); } public void testGetInner () { System.out.println(inner.name); } public class Inner { private final String name; public Inner (String name) { this .name = name; } public void test () { System.out.println(name+", I am inner class!" ); System.out.println(this .name+", I am inner class!" ); System.out.println(Test.this .name+", I am inner class!" ); } } }
1 2 3 4 5 6 7 8 9 10 package org.example;public class Main { public static void main (String[] args) { Test test = new Test ("xwq" ,"will" ); Test.Inner inner = test.new Inner ("weiqiang.xu" ); test.testGetInner(); inner.test(); } }
静态内部类
static
关键字主要用于成员(如变量、方法)、内部类(包括静态内部类)和嵌套类(nested class),以表明它们是属于类本身的,而不是属于类的某个特定实例。
static表明它们是属于类本身 ,但是想要使用内部静态类的变量和方法,还是需要new一个实例对象(类不实例化用个鬼)。
同时static属于类本身的特性也决定了Inner类无法访问Test类的非静态成员。但是受影响的只是外部内容的使用,内部倒是不受影响,还是跟普通的类一样。(我要是直接访问外部类的非静态属性,那到底访问哪个对象的呢?)(内部就是一个普通的类)
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package org.example;public class Test { private final String name; public String getName () { return name; } private final Inner inner; public Test (String name, String innerName) { this .name = name; this .inner = new Inner (innerName); } public void testGetInner () { System.out.println(inner.name); } public static class Inner { private final String name; public Inner (String name) { this .name = name; } public void test () { System.out.println(name+", I am inner class!" ); System.out.println(this .name+", I am inner class!" ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Main { public static void main (String[] args) { Test testInstance = new Test ("xwq" , "will" ); Test.Inner standaloneInner = new Test .Inner("weiqiang.xu" ); testInstance.testGetInner(); standaloneInner.test(); } }
可以明显发现这里的打印是不一样的,因为这里其实有两个Inner实例——
构造test实例时构造的Inner实例
直接构造的Inner实例
对象 vs实例 :大多数情况下这两个术语的语义是相同的,可以平替,都强调是类的具体化
对象更通用,强调创建并存在于内存中的具体实体
实例多用于强调从哪个类创建的,“这个方法接收一个User
类的实例作为参数”
局部内部类 直接在方法中声明的类,只能在该方法中使用。也需要实例化。
1 2 3 4 5 6 7 8 9 10 11 12 public class Test { public void hello () { class Inner { public void test () { System.out.println("我是局部内部类" ); } } Inner inner = new Inner (); inner.test(); } }
匿名内部类
定义与实例化 :提供一种简洁的方式直接定义(实现接口或继承普通类)并实例化类的对象。无需先显式声明类再创建其实例。
多态性 :通过继承或实现接口体现多态,方便重写父类方法或实现接口方法。
适用场景 :适用于仅需一次性使用的对象,减少代码量,提升简洁性。例如,在图形用户界面(GUI)编程中,按钮点击事件的处理程序往往只需要定义一次,使用匿名内部类可以非常方便地完成这一任务。
特点 :
没有名字,只能使用一次。
可访问外部类的所有成员;在方法内定义时可访问该方法内的“effectively final”局部变量。
由于没有名字,不能显式定义构造器,但可以通过其父类构造器传入参数。
eg:实现接口的匿名内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 public abstract class Student { public abstract void test () ; }public static void main (String[] args) { Student student = new Student () { @Override public void test () { System.out.println("我是匿名内部类的实现!" ); } }; student.test(); }
Lambda表达式
1 names.forEach (name -> System .out .println(name ));
方法引用 对Lambda表达式进一步简化,方法引用就是将一个已实现的方法,直接作为接口中抽象方法的实现,前提是定义相同
eg:
1 names.forEach (System .out ::println);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Main { public static void main (String [] args ) { Teacher teacher = System .out ::println; teacher.teach ("will" ); } public interface Teacher { void teach (String name); default void study (String name ) { System .out .println (name+", I am default study method" ); } } }
如果是普通从成员方法,我们同样需要使用对象来进行方法引用
1 2 3 Student student = new Student ();Teacher teacher = student::print; teacher.teach("will" );
工具类
基本数据类型的数组 :使用 Arrays.copyOf
进行复制时,由于基本数据类型存储的是具体的值而不是引用,所以这实际上是进行了深拷贝。原数组和新数组之间没有共享的状态,修改一个数组中的元素不会影响另一个数组。
对象数组 :使用 Arrays.copyOf
对对象数组进行复制时,只会复制对象的引用,而不是对象本身,因此这是浅拷贝。如果对象的内容被修改,这种变化会影响到原数组和新数组中对应的元素,因为它们指向相同的对象。
泛型程序设计
函数式接口 函数式接口特点:有一个抽象方法待实现
Supplier供给型函数式接口 获取需要的对象,只有一个get抽象方法需要实现,简直不要太简单。例子看Consumer中的使用
1 2 3 4 @FunctionalInterfacepublic interface Supplier <T > { T get () ; }
Consumer消费型函数式接口 功能 :andThen对一个对象建立起消费链,accept调用执行消费链。只要明确功能就好了,原理搞不懂也没关系。跟目前适配工作中的config先定义好转换关系,再使用get获取到参数的思想很想——都是先层层嵌套定义好规则,最后一次性调用。
源码如下:
核心:不断实现accept方法来实现消费者链的添加
关键:andThen将先前Consumer的accept行为添加到一个新创建的Consumer中,最后一个Consumer调用accept就能实现整个消费者链的执行
1 2 3 4 5 6 7 8 9 @FunctionalInterface public interface Consumer<T> { void accept (T t); default Consumer<T> andThen (Consumer<? super T> after) { Objects.requireNonNull (after); return (T t) -> { accept (t); after.accept (t); }; } }
andThen()
让我理解了半天,断点debug+理解后终于搞懂了。因为lambda写法特别多,看着很抽象,不好理解具体创建了什么,返回了什么,下面举个例子来解释具体创建的Consumer。
先上结论——在整个链式调用的过程中,确实创建了5个 Consumer
实例:(这里的匿名是在main内来看的,其实符合Consumer也是匿名的)
初始的 APPLE_CONSUMER
(Consumer1
)
第一个匿名 Consumer
(Consumer2
)
第一个复合 Consumer
(Consumer3
)
第二个匿名 Consumer
(Consumer4
)
最终的复合 Consumer
(Consumer5
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Apple { public void hello(){ System.out .println("我是苹果!" ); } private static final Supplier<Apple> APPLE_SUPPLIER = Apple::new ; private static final Consumer<Apple> APPLE_CONSUMER = apple -> System.out .println(apple+"好吃!" ); public static void main(String[] args) { Apple apple = APPLE_SUPPLIER.get(); apple.hello(); APPLE_CONSUMER .andThen(apple1 -> System.out .println(apple1+"1" )) .andThen(apple1 -> System.out .println(apple1+"2" )) .accept(apple); } }
1 2 3 4 APPLE_CONSUMER .andThen (apple1 -> System.out .println (apple1+"1" ))//Consumer1调用andThen,传入Consumer2 .andThen (apple1 -> System.out .println (apple1+"2" )) .accept(apple);
1 2 3 4 5 6 7 8 9 @FunctionalInterface public interface Consumer<T> { void accept (T t); default Consumer<T> andThen (Consumer<? super T> after) { Objects.requireNonNull (after); return (T t) -> { accept (t); after.accept (t); }; } }
1 .andThen (apple1 -> System.out .println (apple1+"2" ))
1 2 3 4 5 6 7 8 9 @FunctionalInterface public interface Consumer<T> { void accept (T t); default Consumer<T> andThen (Consumer<? super T> after) { Objects.requireNonNull (after); return (T t) -> { accept (t); after.accept (t); }; } }
1 .accept (apple)//执行Consumer5的accept
其实最后执行accept的Consumer的accept实际内容精简后是这样的——
1 2 3 4 5 void accept(Apple apple){ System .out .println(apple+"好吃!"); System .out .println(apple+"1")); System .out .println(apple+"2")); }
当然编译器应该不是直接优化成这样的,而是逐层解析对象往下执行的。按照我debug的经验来讲是这样的,事实上也应该是这样。
想起来zstack的chain好像就是消费链模式?
Function函数型函数式接口 这个接口消费一个对象,然后会向外供给一个对象(前两个的融合体)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @FunctionalInterface public interface Function <T, R> { R apply (T t) ; default <V> Function<V, R> compose (Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen (Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity () { return t -> t; } }
直接看怎么用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private static final Function<Integer, String> INTEGER_STRING_FUNCTION = Object::toString; public static void main (String[] args) { String str = INTEGER_STRING_FUNCTION .compose((String s) -> s.length() ) .apply("lbwnb" ); System.out.println(str); boolean result = INTEGER_STRING_FUNCTION .andThen(String::isEmpty) .apply(1 ); System.out.println(result); Function<String, String> function = Function.identity(); System.out.println(function.apply("???" )); }
Predicate断言型函数式接口 接收一个参数,然后进行自定义判断并返回一个boolean结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @FunctionalInterface public interface Predicate <T> { boolean test (T t) ; default Predicate<T> and (Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> negate () { return (t) -> !test(t); } default Predicate<T> or (Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static <T> Predicate<T> isEqual (Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
就不动手敲了,用法比较好懂。
判空包装 Optional的使用,用法可多了
1 2 3 4 5 6 private static void test (String str ){ Optional .ofNullable (str ) .ifPresent (s -> System.out .println ("字符串长度为:" +s.length ())); }
集合类 (这些类的操作纯看和逐个手动实验十分枯燥,没有耐心手敲,非常不想学,总觉得有更重要的事情要做。写代码就是写作,要勇于实践,不能纸上谈兵,一时没有兴趣那就每天抽点时间来写,三分钟热度也要学的更多!!!)
ArrayList 基本操作注意事项
创建方式:三种——创建空列表、指定容量、从collection创建
1 2 3 4 List < Integer > b = new ArrayList <> ( ) ; List < Integer > c = new ArrayList <> ( 5 ) ; List < Integer > d = new ArrayList <> ( b ) ; List < Integer > e = new ArrayList <> ( Arrays . asList ( 1 , 2 , 3 , 4 , 5 ) ) ;
只读与可操作
1 2 List < String > list = Arrays . asList ( "A" , "B" , "C" ) ;// 只读List < String > list = new ArrayList <> ( Arrays . asList ( "A" , "B" , "C" ) ) ;// 可正常操作
add添加操作
remove删除操作:注意删开头第一个后结束
Stream流 工作中遇到再返回来学,先总结几个看到的
filter:条件过滤
collect:收集
sorted:排序
map:对每个元素映射操作
limit:限制个数
1 2 3 4 list = list .stream() .filter (e -> !e.equals ("B" )) .collect(Collectors.toList());