黑马程序员Java基础笔记
类执行顺序
class OrderDemo {
// 1. 类加载阶段:静态成员(按顺序)
static int staticVar = 10;
static {
System.out.println("静态代码块:staticVar=" + staticVar); // 输出10
staticVar = 20;
}
// 2. 实例化阶段:实例成员(按顺序)
int instanceVar = 1;
{
System.out.println("实例代码块1:instanceVar=" + instanceVar); // 输出1
instanceVar = 2;
}
int instanceVar2 = instanceVar + 1;
{
System.out.println("实例代码块2:instanceVar2=" + instanceVar2); // 输出3
}
// 3. 构造方法(最后执行)
public OrderDemo() {
System.out.println("构造方法:staticVar=" + staticVar + ",instanceVar=" + instanceVar);
// 输出:staticVar=20(类加载时修改后的值),instanceVar=2(实例代码块修改后的值)
}
public static void main(String[] args) {
System.out.println("首次创建实例:");
new OrderDemo(); // 触发类加载+首次实例化
System.out.println("\n第二次创建实例:");
new OrderDemo(); // 仅执行实例化阶段(类加载已完成)
}
}
继承
方法重写
子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
当通过对象调用方法时,会优先执行该对象实际类型中重写的方法
Parent p1 = new Parent();
Parent p2 = new Child(); // 父类引用指向子类实例
Child c = new Child();
p1.print(); // 执行Parent的print()
p2.print(); // 执行Child的print()
c.print(); // 执行Child的print()
注意: 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。(why?)
构造器
子类所有构造方法的第一行都会默认先调用父类的无参构造方法
多态
1.子类对象继承的父类类型,或者实现的父接口类型。
2.方法的重写【意义体现:不重写,无意义】
3.父类引用指向子类对象【格式体现】
e.g:
Person.java
public class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
public void show(){
System.out.println("Name: " + name + ", Age: " + age);
}
}
Man.java
public class Man extends Person {
String Sex="Male";
Man(String name, int age) {
super(name, age);
}
@Override
public void show() {
System.out.println("I am man");
}
}
Female.java
public class Female extends Person {
String Sex="Female";
Female(String name, int age) {
super(name, age);
}
@Override
public void show() {
System.out.println("I am Female");
}
}
Main.java
public class Main {
public static void main(String[] args) {
Person p1 = new Man("John", 30);
Person p2 = new Female("lady", 25);
p1.show();
p2.show();
}
}
result:

这便是同一行为,具有多个不同表现形式。
调用特性:
Person P=new man("John", 30);
调用成员变量时:编译看左边,运行看左边
P.Sex;
编译看左边:编译的时候看左边父类中有没有这个变量,没有就报编译错误
运行看左边:运行的时候实际获取的就是父类这个变量的值
结论:对于变量来说子类变量父类不能直接访问,可以通过子类的get/set函数来访问或者向下转型将父类引用转为子类引用后,才能访问子类变量
P.show()
调用成员方法时:编译看左边,运行看右边
编译看左边:编译的时候看左边父类中有没有这个方法,没有就报编译错误
运行看右边:运行的时候实际使用的是子类中重写的方法优先使用
结论:编译器检查变量是否可访问时,只依据引用变量的声明类型(即父类类型),而非运行时的实际对象类型(子类)。实际上运行时能准确指向但是编译时搞不清楚。

抽象类
注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
接口
在JDK7,包括JDK7之前,接口中的只有包含:抽象方法和常量
接口中的抽象方法默认会自动加上public abstract修饰,在接口中定义的成员变量默认会加上: public static final修饰,程序员无需自己手写!!
如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。
** 如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。**
类与接口是实现关系
接口与接口是继承关系
JDK8以后接口中新增默认方法:public default 返回值类型 方法名(参数列表){}
如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
在接口中,被static修饰的方法,不能被重写,可以直接通过接口名调用
如果一个方法中,当参数为接口时,那么在调用方法时就可传递这个接口的所有实现类对象
理解:接口的本质是一份“契约”,它仅定义“必须做什么”(即方法签名),而不规定“具体怎么做”(即方法实现),核心作用是解耦“行为定义”与“行为实现”,实现多模块间的规范统一 。
解耦:接口隔离了“调用者”和“实现者”。调用者只需知道接口定义的方法,无需关心具体是哪个类实现的(如电脑调用 USB 设备,无需管是U盘还是鼠标,只要符合 USB 接口契约就能用),极大提升了代码的灵活性和可扩展性 。
区别:接口只是一个未实现方法的集合,而抽象类语义上有继承关系
思考:如果一个接口中,有10个抽象方法,但是我在实现类中,只需要用其中一个,该怎么办?
可以在接口跟实现类中间,新建一个中间类(适配器类)
让这个适配器类去实现接口,对接口里面的所有的方法做空重写。
让子类继承这个适配器类,想要用到哪个方法,就重写哪个方法。
因为中间类没有什么实际的意义,所以一般会把中间类定义为抽象的,不让外界创建对象
枚举类
枚举类本质是特殊类,枚举常量(如 ALIPAY)就是这个类的“预定义实例”——在枚举类加载时,JVM 会自动创建所有枚举常量,每个常量都是枚举类的一个对象,且全局唯一(不能通过 new 再创建) 。

/* 格式
enum 枚举名 {
枚举常量1(值),枚举常量2(值),……
} */
enum DayOfWeek {
MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);
private int value;
DayOfWeek(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
// 使用
DayOfWeek day = DayOfWeek.MONDAY;
int value = day.getValue();
System.out.println("Today is " + day + ", value is " + value);// 输出Today is MONDAY, value is 1
实现接口的枚举常量
/* 格式
public interface Operation {
int apply(int x, int y);
} */
public enum BasicOperation implements Operation {
PLUS("+") {
public int apply(int x, int y) { return x + y; }
},
MINUS("-") {
public int apply(int x, int y) { return x - y; }
},
TIMES("*") {
public int apply(int x, int y) { return x * y; }
},
DIVIDE("/") {
public int apply(int x, int y) { return x / y; }
};
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override public String toString() {
return symbol;
}
}
// 使用
int result = BasicOperation.PLUS.apply(1, 2); // 3
匿名内部类
// 例如
enum Operation {
PLUS {
public int apply(int x, int y) {
return x + y;
}
},
MINUS {
public int apply(int x, int y) {
return x - y;
}
},
TIMES {
public int apply(int x, int y) {
return x * y;
}
},
DIVIDE {
public int apply(int x, int y) {
return x / y;
}
};
public abstract int apply(int x, int y);
}
// 使用
int result = Operation.PLUS.apply(1, 2) // 返回值为3
字符串




内部类
匿名内部类

{}中才是匿名类,Swim表示实现/继承自它,new表示创建一个实例,()表示调用空参构造器
Swim.java
public abstract class Swim {
int a,b;
public Swim(int a,int b){
this.a=a;
this.b=b;
}
public abstract void swim();
}
Main.java
public class Main {
public static void main(String[] args) {
new Swim(1,2){
@Override
public void swim() {
System.out.println(a+b);
}
}.swim();
}
}
拆箱与装箱
//1.利用构造方法获取Integer的对象(JDK5以前的方式)
/*Integer i1 = new Integer(1);
Integer i2 = new Integer("1");
System.out.println(i1);
System.out.println(i2);*/
//2.利用静态方法获取Integer的对象(JDK5以前的方式)
Integer i3 = Integer.valueOf(123);
Integer i4 = Integer.valueOf("123");
Integer i5 = Integer.valueOf("123", 8);
System.out.println(i3);
System.out.println(i4);
System.out.println(i5);
//3.这两种方式获取对象的区别(掌握)
//底层原理:
//因为在实际开发中,-128~127之间的数据,用的比较多。
//如果每次使用都是new对象,那么太浪费内存了
//所以,提前把这个范围之内的每一个数据都创建好对象
//如果要用到了不会创建新的,而是返回已经创建好的对象。
Integer i6 = Integer.valueOf(127);
Integer i7 = Integer.valueOf(127);
System.out.println(i6 == i7);//true
Integer i8 = Integer.valueOf(128);
Integer i9 = Integer.valueOf(128);
System.out.println(i8 == i9);//false
//因为看到了new关键字,在Java中,每一次new都是创建了一个新的对象
//所以下面的两个对象都是new出来,地址值不一样。
/*Integer i10 = new Integer(127);
Integer i11 = new Integer(127);
System.out.println(i10 == i11);
Integer i12 = new Integer(128);
Integer i13 = new Integer(128);
System.out.println(i12 == i13);*/
克隆
浅克隆:
含义:浅克隆是指在克隆对象时,只复制对象本身和其中的基本类型数据,而不复制对象中引用类型的数据,此时复制出来的对象与原对象共享引用类型数据所在的地址。
特点:当原对象中引用类型数据发生改变时,复制出来的对象也会发生变化。
深克隆:
含义:在克隆对象时,不仅复制对象本身和其中的基本类型数据,还要对对象中的引用类型数据进行递归复制,以确保复制出来的对象与原对象的所有数据完全独立,互不影响
特点:可以保证克隆出来的对象与原对象完全独立,互不影响
注意:深克隆会比浅克隆更加耗费时间和资源,但可以保证克隆出来的对象与原对象完全独立,互不影响。而浅克隆虽然速度快,但是容易出现数据共享的问题,需要特别注意
不会自己写,使用别人的工具类
import com.google.gson.Gson;
public class Test {
public static void main(String[] args) {
Person p1=new Person("Alice", 30);
System.out.println(p1);
Gson gson=new Gson();
//将对象转换为JSON字符串
String json=gson.toJson(p1);
System.out.println(json);
//将JSON字符串转换为对象
Person p3=gson.fromJson(json,Person.class);
p3.setName("bob");
System.out.println(p3);
}
}
正则表达式

public class Test {
public static void main(String[] args) {
String str="sb shisohfs ss";
//获得正则表达式对象
Pattern p= Pattern.compile("sb");
//获取文本匹配器对象
Matcher m = p.matcher(str);
//find()如果有返回true且底层记录子串起始索引和结束索引+1
while(m.find()){
//group()返回字串,调用的是subString()
System.out.println(m.group());
}
}
}
//?表示前面的Java,=表示连接后面的8或11或17但是不包含在结果中,(?i)表示后面字段大小写都匹配
String reg="((?i)Java)(?=8|11|17)";
String reg="((?i)Java)(?:8|11|17)"; //:表示留下在结果中
String reg="((?i)Java)(?!8|11|17)";//!表示不要这样的串
贪婪爬取:ab+,尽可能多的b
非贪婪爬取:ab+?,尽可能少的b


正则内部:\组号
正则外部:$组号

(?:)(?=)(?!)是非捕获分组不占用组号
Lambda



方法引用
简化了Lambda
静态方法引用

实例方法引用

特定类型方法引用

构造器引用

泛型

//泛型类
public class Test <E> {
public Test() { }
public void add(E e){
System.out.println(e.toString());
}
//泛型方法
public static <T> T show(T t){
return (T) t.toString();
}
public static void main(String[] args) {
Test<Person> t= new Test<>();
t.add(new Person());
System.out.println(Test.show("adidas"));
}
}
通配符

泛型只支持引用类型,因为泛型会被擦除,它只工作在编译阶段,编译后它就没有了,所有类型会变为Object,而Object接不了基本类型。
集合

Collection

遍历方式
迭代器


foreach

Lambda

并发修改异常
就是一边遍历一边增删

foreach与Lambda解决不了并发修改异常问题,只适合做遍历。
Set






自定义排序规则:
1.实现Comparable接口,重写comparaTo方法
2.TreeSet自带比较器对象,指定规则。
自定义对象去重

Map

键不重复,同键的值后面覆盖前面的
遍历
1.根据键找值
HashMap<String, Integer> m = new HashMap<String,Integer >();
Set<String> keys=m.keySet();
for(String key:keys){
m.get(key);
}
2.键值对

Map.Entry是接口不是对象,实现类是其内部的Node (key,value)
3.Lambda




Stream流




of(T ... values)中...表示可变参数


Optional是一个含量为1的容器,可以存NULL

File






编码


IO流
I还是O是相对于内存来说的








文件字节输入/输出流不一定比缓冲流慢,速度根据接数据的字节数组大小成正比,超过32KB后速度增长不明显

按照字符去读不会出现中文编码截断问题。


资源释放


缓冲流









打印流


包的BufferWriter

追加包低级的流

特殊流


IO框架(重要)


多线程
创建









常用方法

同步代码



线程池(复用线程)
不会来一个任务就创建一个线程,导致开销巨大





线程工厂是new出线程,然后完成任务




多进程
网络编程

UDP

public class Clients {
public static void main(String[] args) throws Exception {
System.out.println("UDP客户端启动...");
// 创建发送对象
DatagramSocket clientSocket=new DatagramSocket();//随机端口
//构造消息
byte[] massage="再不学习就要G了".getBytes();
//创建数据包
DatagramPacket packet=new DatagramPacket(massage,massage.length, InetAddress.getLocalHost(),8080);
//发送数据包
clientSocket.send(packet);
}
}
public class Service {
public static void main(String[] args) throws Exception {
System.out.println("UDP客户端启动...");
// 创建接受对象
DatagramSocket clientSocket=new DatagramSocket(8080);
//构造接受载体
byte[] massage=new byte[1024*64];//UDP数据包最大64K
//创建接受数据包
DatagramPacket packet=new DatagramPacket(massage,massage.length);
//发接受数据包
clientSocket.receive(packet);
System.out.println(new String(massage,0,packet.getLength()));
System.out.println(packet.getAddress().getHostAddress());
System.out.println(packet.getPort());
}
}
TCP






浙公网安备 33010602011771号