面试1
基础篇
- 如何理解OOP面向对象编程?
对现有事物进行抽象,具有继承、封装、多态的特征。
继承:从已有的类也就是父类进行继承信息。
封装:对数据和数据操作的方法绑定起来,通过方法进行访问或者操作数据。
多态:让不同的子类对于同一消息做出不同的反应。比如父类的引用指向不同的子类,则会使用指向子类的方法。
- 重写和重载的区别?
重写:就是overrider,实现在继承父类方法和实现接口的方法,访问修饰符不能降低,返回值 + 方法名 + 参数 必须一样。
重载:在同一个类中,方法名一样且参数必须不一样,修饰符 + 返回值 + 抛出异样可以任意。
- 接口和抽象类区别?
抽象类:需要子类单继承,可以有构造器和成员变量,抽象方法不可以是private,除了不能进行实例化其余和普通类一样。
接口:需要类进行实现,接口只能是public(默认abstract),没有构造器和成员变量,只能声明常量(默认public static final)。
- 浅拷贝和深拷贝区别?
拷贝就是对象有两种,基本类型和实例对象的引用。
浅拷贝:基本类型OK,拷贝实例对象就是返回对象的引用,实际还是同一个对象。
深拷贝:基本类型OK,拷贝实例对象时会进行对象内容的复制,非同一个对象。
- sleep 和 wait 区别?
sleep:属于Thread中的方法,释放CPU不释放资源。比如sleep(1000)。
wait:属于Object中的方法,释放CPU和资源,使用wait()方法需要使用synchronized(否则抛出IllegalMonitorStateException)。比如wait(1000) 或者 搭配 notify()使用
- 自动拆箱装箱?int和Integer区别?
由于基本类型不具有方法,有时候需要转化为包装类,拆箱装箱就是在两者之间进行转化。自动是因为javac编译器的语法糖,底层通过Integer.valueOf(99) 和 integerObj.intValue() 实现。
int 和 Integer 默认值分别为0 和 null,前者直接存储数据值,后者则是存储的引用地址。
- == 和 equal 区别?
==:如果为基本类型直接比较值,如果为引用类型,比较地址。
equal:Object类中方法也是 == 进行比较地址。比较内容需要重写。
- String为何使用final修饰?
主要是为了安全,final修饰表示字符串具有不可变性,标志线程安全。同时不可变性也提高了效率,如果创建相同的对象,则直接指向同一地址,无需创建新对象;由于不可变性并且hashcode也是不变的。
- StringBuffer 和 StringBuilder 区别?
功能和方法完全等价,比如append,insert,delete等方法。StringBuffer中方法大都采用了synchronized 修饰,表示线程安全但是效率降低,因此可以多线程。StringBuilder 中没有采用,因此线程不安全但效率相对更高,适合单线程。
- final、finally、finalize区别?
final:修饰类:表示不可继承、修饰方法:子类只能使用不可重写、修饰变量:给定初始值之后不再改变。
finally:try…catch…中finally表示最终执行的代码块,无论是否异常都要执行(除非JVM关闭),因此这里可以释放资源。
finalize:Object中的方法,垃圾收集在销毁对象时会执行这个方法,通过重写可以进行整理系统资源和其他清理工作
- Object中有哪些方法?
有部分方法修饰符final、native,请注意。
protected Object clone():
String toString()
boolean equals(Object obj) 、
int hashCode()
void wait()
void notify()
void notifyAll()
Class<? extendsObject> getClass()
protected void finalize()
- 集合体系?
Iterable接口,演化出Collection接口,从而演化出Set接口和List接口,Set接口实现类中有HashSet(其子类为LinkedHashSet),TreeSet;List接口实现类有ArrayList 和 LinkedList 。
Map接口实现类有HashMap(其子类为LinkedHashMap),TreeMap;
- ArrayList 和 LinkedList 区别?
ArrayList:底层为动态数组,随机访问get和set 效率较高。
LinkedList :底层为链表,增删add和remove 效率较高。
- HashMap?
底层的数据结构时 数组 + 链表 + 红黑树。初始时数组为16,通过计算元素的hash值放入相应的位置,冲突则转为链表,当元素到达默认负载因子0.75也就是元素为12的时候,数组开始扩容为原来的2倍,继续增加元素扩容至64时,当其中一个位置也就是桶,当这个桶里面的元素达到8个时转化为红黑树,如果再减小到6,再次转为链表。
- HashMap 和 HashTable 和 ConcurrentHashMap 区别?
HashMap:线程不安全,允许一个null键和多个null值,
HashTable:使用Synchronized所以线程安全。K和V均不许出现null。基本所有方法都是用synchronized。初始为11,扩容则为2n + 1。
ConcurrentHashMap:线程安全。K和V均不许出现null,ConcurrentHashMap性能由于锁的粒度较小,所以比HashTable效率高。java8之前使用Segment从而实现一定的并行性。java8之后采用CAS(Compare-And-Swap)和细粒度锁
CAS:使用大部分是Navtive方法高效。比较内存中的值和预期值是否相等,相等则更新值。否则重新获取内存值设置为预期值,重试直至成功
细粒度锁:对一个桶进行加锁,极大提高并行性
- 线程创建的方式几种?
一般来说就是 3种(本质都是实现Runnable接口) + 线程池
继承Thread类:重写run方法。myThread.start()
实现Runnable接口:重写run方法。Thread(new myRunnable).start()
实现Callable<>接口:重写run方法。 FutureTask(myCallable).run();
线程池:实例化ThreadPoolExecutor(…),执行execute(myThread),然后shutdown关闭线程池。也可以使用并发包中Executors直接调用所需要的线程池。
- 线程的生命周期(状态转化)?
NEW:实例化产生一个新的对象。调用start进入RUNNABLE
RUNNABLE:当获取CPU则为RUNNING,实际取决于CPU的调度。yield方法会让出CPU,RUNNING会转为RUNNABLE
BLOCKED:线程要获取的资源已经被其他线程加锁了;获取资源之后,转为RUNNABLE
WAITING:无限期等待wait()、join()、park(),直至被唤醒进入RUNNABLE
TIMED_WAITING:限时等待sleep(1000)、wait(1000)、join(1000),到达时间或者被唤醒进入RUNNABLE
TERMINATED:线程结束。
- java中有几种类型的流?
主要分为字节流和字符流
字节流:FILE\Buffered + InputStream\OutputStream。FILE可以表示图片等,BufferedInputStream 是InputStream的间接子类(孙子)
字符流:FILE\Buffered + Reader\Writer。FILE用于字符类文件,BufferedReader特有方法readline()。BufferedWriter特有方法newLine(),write(str)。
转化流:继承字符流。InputStreamReader 、OutputStreamWriter字节流转为字符流,
- 常见的RuntimeException,举例
NullPointerException:使用未初始化或者不存在的对象。
IndexOutOfBoundsException:数组越界
ClassNotFoundException:类的路径或者名称错误
ClassCastException:类型转化异常
NumberFormatException:字符串转为数字异常,包含非数字的字符
IllegalArgumentException:方法中参数不符。
- 反射机制?
反射机制就是java在运行时拥有自观的能力,通过这种能力可以了解自身状态,进行下一步操作。Class、Constructor、Method、Field四种类可以粗略的看到类的组成部分。
- java序列化?
序列化是对象转为流的机制,内容就是toString()返回的,进行持久化存储或传输。只需要Serializable接口,仅仅标注此对象可被序列化。但是transient 和 static 不会被持久化,比如transient 修饰密码。
通过一个FileOutputStream构造一个ObjectOutputStream, writeObject(Object obj)就持久化对象。
- 常见的HTTP状态码?
1xx表示信息性、2xx表示成功、3xx表示重定向、4xx表示客户端错误、5xx表示服务器错误。
200表示OK成功,301表示Moved Permanently,302Found临时重定向,400表示Bad Request客户端请求语法错误,401表示Unauthorized ,403表示Forbidden ,500表示Internal Server Error,503表示Server Unavailable
- GET 和 POST 区别?
两者都是底层都是TCP/IP。
GET:通过URL请求,请求的信息在url中,比如login.action?name=lisi&password=123456,明文不安全。请求获取数据,浏览器发送header和data,服务器返回200并返回数据。
POST:通过FORM表单请求,提交的数据在HTTP包的包体中。发送数据到后台,浏览器先发送header,收到服务器的100状态码(Continue),然后在发送data,最后服务器响应200并返回数据。
header:包括元数据,比如Host、Content-Type、Content-Lenghth、User-Agent等。
data:包括具体的参数信息等。
- Cookie 和 Session 区别?
Cookie:是Web服务器发送给Client端的信息,浏览器会在本地存储Cookie。以后再请求此服务器则会发送相应的Cookie。Cookie只存储字符串
Session:存储在Web服务器的信息,session对象存储用户会话所需的属性和配置信息,用户在Web跳转时不会丢失此信息。Session可以存储任何对象。
一般来说Cookie中存储SessionID,发送到Web服务器后,直接根据SessionID使用之前Session,依然显示登陆和偏好之类的。
高级篇
- JVM内存分区,每个区分的作用?
字节码class文件加载到Class loader类装载器系统(双亲委派机制从bootstrap-extension-application)→ 链接 → 初始化。然后和运行时数据区进行交互。运行时数据区有五部分,运行时数据区和本地方法接口和执行引擎三者相互交互,本地方法接口是由本地方法库实现的。
加载:
链接:
- 验证:字节码文件格式,元数据验证,字节码验证
- 准备:给静态变量分配空间并初始化默认值,不执行静态代码块或者静态字段的赋值
- 解析:符号引用(类名、方法名、字段名等)转化为直接引用的过程
运行时数据区的五部分:栈 + PC 线程私有也就是安全的;堆 + metaspace 线程共享,且是JVM调优的主要地区。
-
java栈:每次方法调用就会创建一个栈帧,执行结束后弹出栈帧。栈帧主要由四部分:
- 局部变量表:基本数据类型,引用类型,returnAddress 类型(保存PC指向地址)
- 操作数栈:用于计算操作。
- 动态链接:保存指向运行时元空间的方法和字段引用;支持方法调用中的符号引用到直接应用
- 方法返回地址:返回到调用者的地址
-
本地方法栈:和java栈类似,只不过是navtive方法
-
PC:指向下一条指令
-
堆:存储对象的实际内容
- 新生代:新的实例化对象在eden,s0和s1
- 老年代:新生代15gc后,则进入老年代。老年代中的gc速度赶不上对象生产的速度就会OOM,
-
方法区:也称永久代,Java8之后称为元空间metaspace
- 类的元数据:类的全名、类的修饰符、父类和接口的引用、方法、静态变量
- 常量池:字面量常量、符号引用(其他类、方法、字段的引用,运行时符号引用被解析成直接引用)
- JIT编译后的机器码:比如热点代码,直接执行这些机器码提高效率
执行引擎:作用就是把字节码编译\解释为机器码,操作系统才能执行。
- 解释器:按行解释字节码并执行
- 即时编译器JIT:中间代码生成器、代码优化器、目标代码生成器。分析器
- GC:进行垃圾回收
- 类加载器?
四种:
- Bootstrap:加载java核心类库,无法被java程序直接引用
- Extension:加载java的扩展库。
- Application(System):根据java类的路径加载java类,一般来说java的应用类都是由他来完成。ClassLoader.getSystemClassLoader()获取
- 自定义:继承java.lang.ClassLoader类的方式实现。
java的加载器是按需加载,什么时候用到(类或者接口,其中的方法和字段,子类),什么时候加载。
- JVM中GC策略?
Minor GC:当Eden没有空间,就会此策略。会将Eden和From Survivor转移到To Survivor。且三者默认比例为8:1:1。
Major GC (Full GC):堆整个堆进行回收,JVM此时会暂停所有线程。
- java中垃圾收集器的算法?
复制算法:用于新生代中
标记-清除 和 标记-整理:混合用于老年代,清除会产生碎片,整理不会有碎片。
- 如何判断一个对象是否存活(GC判定方法)?GC roots?
可达性算法:通过GC Roots作为起点,然后向下一直搜索,走过的路径为Reference Chain,当一个对象无法通过Chain链接到GC Roots则认为对象不可用。
引用计数法:不用,容易出现循环而无法回收
GC roots:活跃的java线程、栈的局部变量、类的静态字段、JNI的引用、JVM的内部对象…
- StackOverflowError 和 OutOfMemoryError 什么情况?如何排查?
StackOverflowError:调用方法过多,无线递归,海量的局部变量,Navtive方法中比如 java.net.SocketInputStream.read0 要求分配空间过多。
OutOfMemoryError:死循环创建对象,从数据库中取出过度的数据,对象引用为清空导致JVM无法回收,设置内存太小。
排查:通过jvisualvm进行内存快照分析
- 线程池有哪些? 线程池的优点?
Executors.newSingleThreadExecutor():单个线程按顺序执行,此线程异常结束,则会有新的线程替代。
Executors.newFixedThreadPool(5):指定线程的个数
Executors.newCachedThreadPool():创建线程数量没有限制(Interger. MAX_VALUE),虽然可以灵活的增加线程,过多的线程造成系统崩溃
Executors.newScheduledThreadPool(5):指定线程的个数,可以周期性执行任务,比如延迟2秒执行。
上面返回的是ExecutorService接口,底层还是通过ThreadPoolExecutor类进行操作。
优点:
- 降低资源消耗,提高速度:不需要在创建新的线程,直接使用
- 提高线程的管理型:线程是稀缺资源,过多线程会造成系统崩溃,线程池可以对线程进行统一分配,调优和监控
- 线程池的状态?
Running:创建或者使用execute()进入此状态
Shutdown:调用shutdown(),不再接受新的任务,但线程池执行完队列。
Stop:调用shutdownNow(),立即停止执行的任务。
Tidying:由Shutdown和Stop转来,不做任何处理。
Terminated:由Tidying转来,所有线程都终止
- 线程池工作原理?
- 线程池实例化后,有任务过来才开始创建线程;也可以预先创建线程prestartCoreThread() 或 prestartAllCoreThreads()
- 调用execute()执行任务时,如果工作核心线程小于设定的,则创建核心线程并执行
- 如果核心线程已经最大,则进入等待队列
- 如果队列已满,但是maximumPoolSize未满,则开启新线程执行任务。如果maximumPoolSize满了抛出RejectedExecutionException
- ThreadPoolExecutor对象中参数?
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
阻塞队列BlockingQueue接口参数:
- ArrayBlockingQueue<>(5):数组,有界
- LinkedBlockingQueue<>():链表,默认无界,可以给参数设定为有界。
- SynchronousQueue<>():无缓冲队列,来一个任务必须有线程执行,没有空闲线程,则任务堵塞
线程工厂ThreadFactory:
- 直接实现ThreadFactory接口,创建Thread
- Executors.defaultThreadFactory() 获取线程工厂
拒绝策略RejectedExecutionHandler:当队列满,且线程数量满,提交新的任务会拒绝
- AbortPolicy:默认策略,直接抛出RejectedExecutionExcepiton
- CallRunsPolicy:直接有提交任务的线程执行此任务。
- DiscardPolicy:直接丢弃
- DiscardOldestPolicy:丢弃最旧的任务,然后提交当前任务
如果CPU密集型,说明一直需要CPU执行任务,所以 线程数 = CPU个数。
如果是IO密集型,则可以多设置点线程,比如 线程数 = 2*CPU个数。
线程池最佳线程个数 = (等待时间 + CPU执行时间) / CPU执行时间 * CPU个数
- 常见线程安全的并发容器?
CopyOnWriteArrayList、CopyOnWriteArraySet:通过复制保证线程安全。
ConcurrentHashMap:上面说过
- Atomic原子类?
java中的原子类存放在JUC下的atomic下。
基本类型:AtomicInteger、AtomicLong、AtomicBoolean
数组类型:AtomicIntegerArray、AtomicLongArray
引用类型:AtomicReference、AtomicMarkableReference
AtomicInteger 通过使用CAS + volatile + native实现,避免synchronized 高开销,提升了效率
- synchronized 和 Lock 区别?
synchronized | ReentrantLock |
---|---|
关键字,JVM层面的锁(更底层C++实现) | 类,API层面的锁 |
自动加锁、自动释放 | 手动上锁,手动释放 |
非公平锁 | 公平锁、非公平锁 |
不可中断 | 可中断,tryLock(long timeout,timeUnit unit); lockInterruptibly() |
锁的对象,锁信息保存在对象中 | int类型state来标识锁的状态 |
底层有锁升级过程:偏向锁 、轻量级锁、重量级锁 | 无升级 |
- synchronized 和 volatile 区别?
volatile:保证变量的可见性,但线程不安全。不阻塞。仅仅修饰变量。且禁止指令重排,JVM优化没了
synchronized :保证变量的可见性和线程安全。会阻塞。可以修饰变量、方法、块。
synchronized 方法:无static则一个实例一把锁,有static则所有实例一把锁。
- 死锁?java中如何查看死锁?
死锁的四个必要条件:互斥、请求与保持、不可剥夺、循环等待。
死锁检测:确保第一时间发现并解决。图算法查看是否死锁
解决死锁,破坏其中一个条件即可:注意加锁顺序,加锁设上时间。
死锁预防:银行家算法
jsp:可以产看对应进行,比如死锁进程为pid22080,
jstack 22080:可以查看信息
面试2
- 为什么不建议使用Executors.newFixedThreadPool创建线程池
通过查看源码,阻塞队列默认为LinkedBlockingQueue,由于没有设置多少个任务,默认会有Integer.MAX_VALUE。容易造成内存溢出。
- ThreadLocal?应用场景
每一个线程拥有一个ThreadLocalMap,且Entry中key为ThreadLocal,value则为ThreadLocal存储的值。通过ThreadLocal.set(value)可以在map中存储一个对应的entry。
应用场景:A、B、C如果同时使用一个信息,则可以使用ThreadLocal共享变量。存储线程不安全,比如SimpleDataFormat
简介
Sun公司发明Java,后面Oracle收购Sun。所以Java在Oracle官网。
Java分为JavaSE、JavaEE、JavaME。现在javaME被Android占领。
JDK包括JRE,JRE包括JVM
Java的语言类型:java准确来说属于混合型语言,但更偏向于解释型。
编译:java存在JIT和AOT,JIT是以字节码程序存在,并且将可将热点代码直接编译成机器码(本地代码),AOT可在安装时把代码编译成机器码。并且安装时一般不带有源码*.java
,安装好是以字节码或者机器码存在的。
解释:java运行时需编译成class文件,JVM在解释class文件。
环境配置:
- JAVA_HOME
- path:%JAVA_HOME%\bin,%JAVA_HOME%\jre\bin
jdk文件夹:
- bin、include、lib
- src.zip\java存放Java核心库源代码。
- jre\lib\rt.jar是运行时的核心库相应的字节码,同上。
测试:java -version
project -> module -> package -> class
心代码库rt.jar:
- text、beans、security
- rmi:
- nio:new IO非阻塞
- applet,Apple是采用Java编程语言编写的小应用程序,该程序可以包含在 HTML(标准通用标记语言的一个应用)页。
常用命令
javac HelloWorld.java # 生成HelloWorld.class 字节码
java HelloWorld # 运行字节码# @param,return,throws,author,version,since
javadoc HelloWorld.class jar cf Hello.jar HelloWorld.class # 生成jarjps # 产看java运行的进程状态
jstatck pid # 产看某进行的栈信息jconsole # 打开 Java监视和管理控制台
javadoc -d doc -encoding utf-8 MyClass.java
命令产生文档
package org.example;/*** 这里是类的 Javadoc 注释* 可以简要描述类的作用和功能* * @author 张三* @version 1.0* @since 2025-01-13*/
public class MyClass {/*** 这是方法的 Javadoc 注释* * @param name 传入的姓名* @return 返回问候语*/public String greet(String name) {return "Hello, " + name;}
}
类
static方法
static方法:类本身的方法。有无实例均可使用。
// Demo1:
class Parent {// 此方法,可继承使用 或 隐藏,不可重写;public static void say(){System.out.println("Parent hello");}
}
class Child extends Parent{// 这种是“隐藏hiding”,而非“重写override”public static void say(){System.out.println("Child hello");}
}
// 每个类调用本身(非实例)的方法
Child.say(); // 如果子类没有方法say(),则调用父类static方法。
Parent.say();
// parent.say() 编译错误// 拓展Demo2: 变量隐藏:子类和父类同名变量,覆盖父类的此变量
class Parent {String name = "Parent";void printName() {System.out.println("Parent name: " + name);}
}class Child extends Parent {String name = "Child"; // 变量隐藏void printName() {System.out.println("Child name: " + name);}void printBothNames() {System.out.println("Child name: " + name);System.out.println("Parent name: " + super.name);}
}public class Main {public static void main(String[] args) {// 父类引用(即使指向子类对象)访问变量时,使用的是父类的变量。Parent parentRef = new Child();System.out.println("ParentRef.name: " + parentRef.name); // 输出: ParentparentRef.printName(); // 输出: Child name: ChildparentRef.name; // 注意区分:值为: ParentparentRef.printBothNames(); // 输出: Child Parent}
}
// 变量的绑定是静态绑定,编译期间就确定了
// 方法是动态绑定(运行时绑定)
常用的静态方法,也就是工具类
# util中
Arrays.sort(numsarry);
Arrays.toString(numsarry);
final
final:修饰类 、方法、变量
class Parent {// final修饰方法:子类直接用,无法重写public final void display() {System.out.println("Parent's final method");}
}// final修饰的类不可继承,比如String、Integer、UUID
protected方法
类中protected方法:比如自己包内的Demo类,是无法使用的
// 不同包,直接通过父类调用protected方法,编译错误// 如果想要调用,则需要写一个子类继承这个类,然后子类通过改方法为public型即可
tip:面试clone()为啥需要重写(提示:protected)?
构造器
- 如果父类有无参构造器,子类默认调用
// Demo1: 无参构造器,
class Parent {public Parent() {System.out.println("Parent constructor");}
}class Child extends Parent {// 默认构造器会隐式调用 super()
}public class Main {public static void main(String[] args) {new Child(); // 输出: Parent constructor}
}// Demo2: 有参数构造器
class Parent {public Parent(String name) {System.out.println("Parent constructor: " + name);}
}class Child extends Parent {// 必须显式调用父类构造器。否则编译报错public Child(String name) {super(name);}
}public class Main {public static void main(String[] args) {new Child("Child"); // 输出: Parent constructor: Child}
}
代码块执行顺序:静态代码块 > 初始化代码块 > 构造器
class MyClass {// 静态代码块,类加载时执行,只执行一次。static {say("static");}// 实例初始化代码块,每次创建对象时执行,优先于构造器。{say("default");}
}
接口
interface MyInterface {// 只有一个抽象方法,为函数式接口,可以使用lambda/*MyInterface myInterface2 = () -> System.out.println("Ha...");myInterface2.say();*/void say();// 只能这样用 MyInterface.staticMethod(); 且无法重写static void staticMethod() {System.out.println("Static method in interface");}// 必须加上default,仅供实例使用。default void defaultMethod() {System.out.println("Default method in interface");}
}class MyClass implements MyInterface {// 可以选择实现接口的普通方法和default方法,@Overridepublic void say() {}
}public class Main {public static void main(String[] args) {// 创建接口的实现类实例MyInterface obj = new MyClass(); // obj.staticMethod(); // 错误,编译不通过// 调用接口的默认方法obj.defaultMethod(); // 正确调用}
}
lambda表达式
函数式接口(Functional Interface)是仅 一个抽象方法 的接口,函数式接口可以有多个 默认方法 或 静态方法,
常用:
Runnable
:run()
,适用于没有参数和返回值的任务。Consumer<T>
:accept(T t)
,用于处理类型为T
的对象(不返回值)。Supplier<T>
:get()
,用于提供类型为T
的对象。Function<T, R>
:apply(T t)
,用于将一个对象转换为另一个对象。Comparator<T>
:compare(T o1, T o2)
,用于定义对象的比较逻辑。
Demo:
// Demo1: 线程
new Thread(() -> System.out.println("Hello World")).start();// Demo2: Consumer<T>
Consumer<String> stringConsumer = s -> System.out.println("Consumer: " + s);
stringConsumer.accept("Hello World");// Demo3: Supplier<T>
Supplier<Integer> integerSupplier =() -> Integer.valueOf(123);
System.out.println(integerSupplier.get());// Demo4: Function<T, R>。T:type,R:result。亦可(String s) -> s.length()
Function<String, Integer> stringIntegerFunction = s -> s.length();
System.out.println(stringIntegerFunction.apply("hello"));// Demo5: Comparator<T>
String[] names = {"Alice", "Tom", "Bob"};
// 等效Comparator<String> stringComparator = String::compareTo;
Comparator<String> stringComparator = (s1, s2) -> s1.compareTo(s2);
Arrays.sort(names, stringComparator); // 列表:Collections.sort(names, ...);
System.out.println(Arrays.toString(names));// forEach中lambda
list.forEach(item -> System.out.println("===" + item));
map.entrySet().forEach((entry) -> System.out.println(entry.getKey() + entry.getValue()));
注解
常用注解
- @Override 实现父类或接口的方法
- @Deprecated 标记过时的 类、方法、字段
- @SuppressWarnings 抑制编译器的警告信息,尽量少用。比如@SuppressWarnings(“unchecked”)
- @FunctionalInterface 函数式接口
- @Retention 保留,参数可选SOURCE,CLASS,RUNTIME
- @Target 用于标注注解的注解,表示可以使用的地方T,参数有TYPE、FIELD、METHOD…
- 自定义注解
// Demo1: 自定义注解
@Target(ElementType.TYPE)
@interface MyAnnotation {String value() default "Defalut";
}@MyAnnotation(value = "2")
public class Demo {}
常用类
java.langObject、Throwable、Exception、ErrorString\StringBuffer\StringBuilderBoolean、Character、Byte\Short\Integer\Long\Math、Float\Double Class\ClassLoader、Runnable\Thread\ThreadDeathSystem\Runtimejava.utilDate\Calendar\GregorianCalendar*List\*Set\*Map\VectorRandomjava.io*InputStream\*OutputStream\*Write\*Read、Filejava.netURL*/Socket*/Cookie*
java.lang
基本数据类型
long num1 = 1L; // 1默认类型为int
float f1 = 1.0f; // 1.0默认类型为double
double d1 = 1.0; // 1D表示1.0,
int oct = 0123; //八进制
int hex = 0xaa // 16进制// 常见范围
Integer.SIZE;
Integer.MIN_VALUE;
Integer.MAX_VALUE;/* 常见默认值:char '\u0000'3.14E3 是3140,3.14E-3 是0.00314
*/// 常见的方法
int a = Integer.parseInt(str);
dobule b = Double.parseDouble(str);// String
String str = null;
System.out.println(str); // 输出 null
System.out.println(str.length()); // NullPointerException// Integer.valueOf()源码。包装类a=1 b=1指向同一地址
public static Integer valueOf(int i) {assert IntegerCache.high >= 127;if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);
}int a=10;
int b=20;
System.out.println(""+a+b); //1020
System.out.println(a+b+""); //30
Object
// Demo1:
// 有方法 hashCode(), equals(),toString()
// 使用 toString 方法。输出:对象包名@hash值
// 等效System.out.println(obj1);在sout才会自动调用toString()// Demo2:
// clone(), 在Object中修饰符是 protected,需要重写。
public class MyClass implements Cloneable { // 接口Cloneable,象征性的表示可以克隆private int value;public MyClass(int value) {this.value = value;}//显式调用父类的 clone 方法,这样子类Demo才可以使用父类的clone();@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone(); // 这里是浅拷贝,同一个数据的不同引用// 深拷贝,复制数组。/* Demo cloned = (Demo) super.clone(); cloned.values = Arrays.copyOf(this.values, this.values.length);return cloned;*/}@Overridepublic String toString() {return "value: " + value;}
}public static void main(String[] args) {try {MyClass original = new MyClass(42);MyClass copy = (MyClass) original.clone();System.out.println(original);System.out.println(copy);} catch (CloneNotSupportedException e) {e.printStackTrace();}
}
Throwable
public class ThrowableExample {public static void main(String[] args) {try {throw new Throwable("This is a Throwable example");} catch (Throwable t) {System.out.println("Caught Throwable: " + t.getMessage());t.printStackTrace();// 加下面代码,则直接结束方法,抛出异常,下面sout不会被打印,// throw new RuntimeException(t);}System.out.println("=====");}
}
Exception
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;public class ExceptionExample {public static void main(String[] args) {try {// 试图读取一个不存在的文件File file = new File("nonexistent_file.txt");Scanner scanner = new Scanner(file);} catch (FileNotFoundException e) {System.out.println("Exception caught: " + e.getMessage());}}
}
NullPointerException
public class NullPointerExample {public static void main(String[] args) {String str = null; // str 未初始化System.out.println(str.length()); // 尝试调用方法,抛出 NullPointerException // 解决思路:if判断是否null, 或者捕获异常 String[] strings = new String[5];System.out.println(strings[0].length()); // 同样,抛出 NullPointerException }
}
IllegalArgumentException
public class Demo {public static void main(String[] args) {printAge(-1);}public static void printAge(int age) throws IllegalArgumentException{if (age < 0){throw new IllegalArgumentException("age Exception :" + age);}System.out.println("age: " + age);}
}
Error
// 无限递归,Error也可用catch捕捉
String、StringBuffer、StringBuilder
特性 | String | StringBuffer | StringBuilder |
---|---|---|---|
是否可变 | 不可变 | 可变 | 可变 |
线程安全性 | 是(不可变实现线程安全) | 是(同步机制synchronized实现线程安全) | 否(不支持同步) |
性能 | 最慢 | 较快(线程安全开销) | 最快 |
使用场景 | 少量字符串修改 | 多线程环境频繁修改 | 单线程环境频繁修改 |
public class StringExample {public static void main(String[] args) {String s1 = "Hello";String s2 = s1 + " World"; // 生成新对象// 方法 append() insert() delete() replace() reverse()StringBuffer sb = new StringBuffer("Hello");System.out.println("StringBuffer result: " + sb.toString()); // StringBilder同上}
}
特性 | String s = new String("123") | String s = "123" |
---|---|---|
创建对象个数 | 每次创建新对象 | 常量池中存在,直接引用 |
存储位置 | 堆内存 | 字符串常量池 |
是否共享对象 | 不共享 | 共享(如果内容相同),推荐使用 |
String类重写了equals,源码中是比较内容
基本数据类型和其包装类
public class BooleanExample {public static void main(String[] args) {Boolean isJavaFun = true; // 基本类型Boolean isFishTasty = Boolean.valueOf("false"); // 包装类char c = 'A';Character ch = Character.valueOf(c);// 判断字符类型System.out.println(Character.isLetter(ch)); // trueSystem.out.println(Character.isDigit(ch)); // falseSystem.out.println(Character.toLowerCase(ch)); // abyte b = 10;Byte byteObj = Byte.valueOf(b);System.out.println("Byte value: " + byteObj); // 10System.out.println("Max Byte: " + Byte.MAX_VALUE); // 127System.out.println("Min Byte: " + Byte.MIN_VALUE); // -128short s = 1000;Short shortObj = Short.valueOf(s);System.out.println("Short value: " + shortObj); // 1000System.out.println("Max Short: " + Short.MAX_VALUE); // 32767System.out.println("Min Short: " + Short.MIN_VALUE); // -32768 int num = 12345;Integer intObj = Integer.valueOf(num);System.out.println("Integer value: " + intObj); // 12345System.out.println("Binary: " + Integer.toBinaryString(num)); // 11000000111001System.out.println("Max Integer: " + Integer.MAX_VALUE); // 2147483647System.out.println("Min Integer: " + Integer.MIN_VALUE); // -2147483648long l = 123456789L;Long longObj = Long.valueOf(l);System.out.println("Long value: " + longObj); // 123456789System.out.println("Hexadecimal: " + Long.toHexString(l)); // 75bcd15System.out.println("Max Long: " + Long.MAX_VALUE); // 9223372036854775807System.out.println("Min Long: " + Long.MIN_VALUE); // -9223372036854775808System.out.println("Absolute value: " + Math.abs(-5)); // 5System.out.println("Square root: " + Math.sqrt(16)); // 4.0System.out.println("Power: " + Math.pow(2, 3)); // 8.0System.out.println("Random number: " + Math.random()); // 0.0 to 1.0System.out.println("Max: " + Math.max(10, 20)); // 20float f = 3.14f;Float floatObj = Float.valueOf(f);System.out.println("Float value: " + floatObj); // 3.14System.out.println("Max Float: " + Float.MAX_VALUE); // 3.4028235E38System.out.println("Min Float: " + Float.MIN_VALUE); // 1.4E-45double d = 3.14159265359;Double doubleObj = Double.valueOf(d);System.out.println("Double value: " + doubleObj); // 3.14159265359System.out.println("Max Double: " + Double.MAX_VALUE); // 1.7976931348623157E308System.out.println("Min Double: " + Double.MIN_VALUE); // 4.9E-324}
}
tip:包装类中很多static方法可以直接调用,Integet,、String、Character、Arrays,等
整数
public class IntegerExample {public static void main(String[] args) {int num = 12345; // 基本数据类型Integer intObj = Integer.valueOf(num); // 包装类对象// 比较两者System.out.println("num: " + num); // 输出 12345System.out.println("intObj: " + intObj); // 输出 12345// 使用包装类的方法System.out.println("Binary of intObj: " + Integer.toBinaryString(intObj)); // 输出 11000000111001// 判断是否同一个对象Integer a = Integer.valueOf(100); // 在 -128 ~ 127 范围,返回缓存对象Integer b = Integer.valueOf(100);System.out.println(a == b); // true(引用相等,因为使用了缓存)Integer c = Integer.valueOf(200); // 超出缓存范围Integer d = Integer.valueOf(200);System.out.println(c == d); // false(引用不相等,创建了新的对象)}
}
Runnable
class MyTask implements Runnable {@Overridepublic void run() {System.out.println("Task is running: " + Thread.currentThread().getName());}
}public class RunnableExample {public static void main(String[] args) {MyTask task = new MyTask();Thread thread = new Thread(task); // 将 Runnable 对象传递给 Threadthread.start(); // 启动线程。如果使用thread.run();就是普通方法}
}
Thread
class MyThread extends Thread {@Overridepublic void run() {System.out.println("Thread is running: " + getName());}
}public class ThreadExample {public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程thread.join(); // 等待此线程执行结束,再向下执行。 }
}
Callable<>
class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("hello world");return "hello";}
}FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
new Thread(futureTask).start();
System.out.println(futureTask.get());
线程中断
public class Test1 implements Runnable{volatile boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new Test1());thread.start();Thread.sleep(1000);thread.interrupt();}@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()){System.out.println("thread is executing");}System.out.println("thread is interrupted");}
}
ThreadDeath
public class ThreadDeathExample {public static void main(String[] args) {Thread thread = new Thread(() -> {try {while (true) {System.out.println("Running...");Thread.sleep(500);}} catch (ThreadDeath td) {System.out.println("ThreadDeath caught! Cleaning up resources...");throw td; // 重新抛出以终止线程} catch (InterruptedException e) {System.out.println("Interrupted!");}});thread.start();try {Thread.sleep(2000); // 主线程等待 2 秒} catch (InterruptedException e) {e.printStackTrace();}thread.stop(); // 强制终止线程,不推荐使用}
}
ClassLoader
public class ClassLoaderExample {public static void main(String[] args) {// 获取 ClassLoaderClassLoader classLoader = ClassLoaderExample.class.getClassLoader();System.out.println("应用类加载器: " + classLoader);ClassLoader parent = classLoader.getParent();System.out.println("扩展类加载器: " + parent);ClassLoader bootstrap = parent.getParent();System.out.println("引导类加载器: " + bootstrap); // 通常为 null,因为由本地代码实现/* $ 表示子类应用类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2扩展类加载器: sun.misc.Launcher$ExtClassLoader@74a14482引导类加载器: null*/}
}
Class
此类是 Java 反射机制的核心部分
public class ClassExample {public static void main(String[] args) throws Exception {// 获取 Class 对象的三种方式Class<?> cls1 = Class.forName("java.lang.String"); // 通过类名Class<?> cls2 = String.class; // 通过 .classClass<?> cls3 = "hello".getClass(); // 通过对象实例// 比较三个 Class 对象System.out.println(cls1 == cls2); // trueSystem.out.println(cls2 == cls3); // true// 获取类的信息System.out.println("类名: " + cls1.getName());System.out.println("类名: " + cls1.getSimpleName());System.out.println("包名: " + cls1.getPackageName());// 通过反射创建实例Object obj = cls1.getConstructor(String.class).newInstance("Dynamic String");System.out.println("创建的实例: " + obj);}
}
既然提到了Class,就说说反射。
反射
- 优势:灵活(可以动态访问运行时的类和其方法),许多框架就使用反射技术实现自动化配置和依赖注入。
- 缺点:性能开销(由于动态访问),不安全(可以访问private属性)
class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public void greet() {System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");}public void greet(String content) {System.out.println("Hello " + content);System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");}
}public class ReflectionExample {public static void main(String[] args) throws Exception {// 获取 Person 类的 Class 对象Class<?> clazz = Class.forName("com.adair.gpt.Person");// 获取类的构造方法Constructor<?> constructor = clazz.getConstructor(String.class, int.class);Person bob = (Person)constructor.newInstance("Bob", 18);// 获取所有字段Field[] fields = clazz.getDeclaredFields();// 获取name字段Field name = clazz.getDeclaredField("name");name.setAccessible(true); // 设置为可接近,如果类中不是private,则不需要设置System.out.println(name.get(bob));name.set(bob, "Alice");System.out.println(name.get(bob));// 获取所有方法Method[] methods = clazz.getDeclaredMethods();// 获取特定的方法Method greet = clazz.getMethod("greet");greet.invoke(bob);Method greet2 = clazz.getMethod("greet", String.class);greet2.invoke(bob, "World");}
}
System:
// 获取系统时间。可以用于测试程序的时间。endTime - startTime
long currentTime = System.currentTimeMillis();
// 获取系统属性
String javaLibrary = System.getProperty("java.library.path");
// 获取环境变量
String path = System.getenv("Path");
System.getenv("windir");int[] source = {1, 2, 3, 4, 5};
int[] destination = new int[5];
// 方便 int[] ints = Arrays.copyOf(source, source.length);
System.arraycopy(source, 0, destination, 0, source.length);// 调用垃圾回收 和 退出程序
System.gc();
System.exit(0);
package com.gpt;import java.io.IOException;public class RuntimeExample {public static void main(String[] args) {// 单例模式Runtime runtime = Runtime.getRuntime();// 获取 JVM 内存信息。 总内存(可变)即开始运行的初始堆的大小。总内存固定。// 如果程序需要更多内存,JVM 总内存会自动扩展内存,直到达到最大内存的限制。System.out.println("JVM 总内存:" + runtime.totalMemory());System.out.println("JVM 可用内存:" + runtime.freeMemory());System.out.println("JVM 最大内存:" + runtime.maxMemory());// OutOfMemoryError 即超过最大内存/*打开VMoptions 修改参数,可以修改JVM内存-Xms512m # 设置初始堆内存为 512MB-Xmx2g # 设置最大堆内存为 2GB* */// 垃圾回收runtime.gc();// 执行外部命令try {Process process = runtime.exec("notepad"); // 在 Windows 上打开记事本Process process3 = runtime.exec("calc"); // 在 Windows 上计算器} catch (IOException e) {e.printStackTrace();}}
}
java.util
Date\Calendar\GregorianCalendar
public class Demo2 {public static void main(String[] args) {// 过时的,尽量不用Date date = new Date();System.out.println(date);// Calendar.getInstance();创建GregorianCalendar对象Calendar instance = Calendar.getInstance();// 输出很多属性, 远超DateSystem.out.println(instance);System.out.println(instance.get(Calendar.YEAR));System.out.println(instance.get(Calendar.MONTH) + 1); // 月份从0开始instance.set(2024, Calendar.JANUARY, 1); // 源码中JANUARY为0System.out.println(instance.get(Calendar.MONTH) + 1);// GregorianCalendar是Calendar的子类。提供比如判定闰年更为细致的操作}
}
*List
// 推荐下面写法。父类引用指向子类
List<String> list = new ArrayList<>();
// 1.面向接口编程,2.增加其灵活性。如果要修改ArrayList为LinkedList,方便
List包括ArrayList、LinkedList。有序,可重复
public class ListExample {public static void main(String[] args) {// List<String> list2 = new ArrayList<>(Arrays.asList("Apple", "Banana", "Cherry"));List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Apple"); // 允许重复// 获取元素System.out.println("First element: " + list.get(0));// 删除元素list.remove(1);// List,和Set可以使用iterator()进行遍历// 如果要遍历后删除特定元素,使用iterator,而不要使用foreachIterator<String> iterator = list.iterator();while (iterator.hasNext()){if (iterator.next().equals("Banana")){iterator.remove(); }}/*为什么使用for-each会导致删除出现异常?for-each本质也是用iterator进行遍历集合,如果通过集合的方法(比如remove)修改集合,会导致集合结构被修改(modCount值发生变化)。从而触发ConcurrentModificationException。这是快速失败(fail-fast)机制,用于避免迭代器在集合结构被修改后行为不一致。/*}
}
数组到列表:最普通的方式,循环遍历加入list
// Demo1 : String类,或者 非基本数据类型
String[] array = {"a", "b", "c"};
// 转化后的list固定大小的,只能修改元素,不能增删元素。源码AbstractList中的add()方法抛出异常
List<String> list = Arrays.asList(array);// 可变。ArrayList重写了AbstractList中的add()方法
List<String> arrayList = new ArrayList<>(Arrays.asList(array));
List<String> arrayList = new ArrayList<>(Arrays.asList("123", "abc"));// 同上
ArrayList<String> arrayList = new ArrayList<>();
Collections.addAll(arrayList, array); // 通过工具类进行操作/*
拓展: Arrays.asList(array)得到的List不可变?
sout(list.getClass().getName())可以得到java.util.Arrays$ArrayList
这个是ArrayList是Arrays的private类。因此最好也使用List接口去使用
而不是java.util.ArrayList*/// Demo2: 基本数据类型
int[] arr = {1, 2, 3};
/*
由于int[]作为一个整体对象。如果是Integer[] 则为 ArrayList<Integer>
ArrayList<int[]> ints = new ArrayList<>(Arrays.asList(arr));
*/
List<Integer> collect = Arrays.stream(arr).boxed().collect(Collectors.toList());// Demo: int[]转化为List<Integer>int[] arr = {1, 2, 5, 3};
// 转为基本流,还有其他LongStream。对象流Stream<Object>
IntStream stream = Arrays.stream(arr);
// 每个原始 int转换为对应的 Integer对象。Stream<Integer> integerStream = Stream.of(1, 2, 3);
Stream<Integer> boxed = stream.boxed();
// Collectors.toList()会返回List,从而被其收集。 List<Integer> collect =
List<Integer> collect = boxed.collect(Collectors.toList());// 或者
stream.forEach(list::add);
Set包括HashSet、LinkedHashSet、TreeSet。无序,不重复
public class SetExample {public static void main(String[] args) {// Set<String> set2 = new HashSet<>(Arrays.asList("Apple", "Banana", "Cherry"));Set<String> set = new HashSet<>();set.add("Apple");set.add("Banana");set.add("Apple"); // 重复元素不会添加// 检查元素System.out.println("Contains Banana: " + set.contains("Banana"));// 遍历for (String item : set) {System.out.println("Item: " + item);}}
}
Map包括 HashMap、LinkedHashMap、TreeMap。键唯一,值可重复
public class MapExample {public static void main(String[] args) {/* Map<Integer, String> map2 = new HashMap<Integer, String>() {{put(1, "Apple");put(2, "Banana");put(3, "Cherry");}};实际创建一个HashMap子类的匿名内部类,然后执行双大括号的实例初始化块。ArrayList也可以,不推荐*/Map<Integer, String> map = new HashMap<>();map.put(1, "Apple"); // 推荐这种方法。map.put(2, "Banana");map.put(1, "Orange"); // 键重复时会覆盖旧值System.out.println("Map: " + map);// 获取值System.out.println("Value for key 1: " + map.get(1));// 遍历// map.entrySet().forEach((entry) -> System.out.println(entry.getKey() + entry.getValue()));for (Map.Entry<Integer, String> entry : map.entrySet()) {System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());}// 把map转变为set(存储entry)后再iterator遍历Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();while (iterator.hasNext()){Map.Entry<Integer, String> next = iterator.next();System.out.println(next.getKey() + "---" + next.getValue() );}}
}
Vector
public class VectorExample {public static void main(String[] args) {Vector<String> vector = new Vector<>();vector.add("Apple");vector.add("Banana");vector.add("Cherry");System.out.println("Vector: " + vector);// 获取元素System.out.println("First element: " + vector.get(0));System.out.println("First element: " + vector.elementAt(0));// 删除元素vector.remove(1);System.out.println("After removal: " + vector);}
}
Random:不是线程安全的
- 多线程或者频繁使用ThreadLocalRandom
- 更加安全的类型SecureRandom
// 默认种子为System.nanoTime();
Random random = new Random();// 各种数据类型都可以随机
System.out.println(random.nextBoolean());
System.out.println(random.nextInt(100));// 填充byte[]
byte[] arr = new byte[5];
random.nextBytes(arr);
System.out.println(Arrays.toString(arr));
Scanner
- 实际项目:如果要输出int,先判断是不是在输出。
- nextLine问题:在调用next() 或者 nextInt() 后要处理回车
- 使用后进行close,尤其是文件
// 从键盘中获取
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine(); // 等待回车,表示一行
System.out.println("Hello " + s);// 从字符中读取
Scanner scanner1 = new Scanner("Java 123 3.14 true");
System.out.println(scanner1.next()); // 以空格和回车为分割
System.out.println(scanner1.nextInt());
System.out.println(scanner1.nextFloat());
System.out.println(scanner1.nextBoolean());// 从文件中读取
Scanner scanner = new Scanner(new File("data.txt"));
while (scanner.hasNextLine()){System.out.println(scanner.nextLine());
}scanner.close();
File
// 常用方法。exists,createNewFile, getAbsolutePath,delete
File file = new File("example.txt");
if (!file.exists()){if(file.createNewFile()){System.out.println("File created " + file.getAbsolutePath());}else {System.out.println("File creation failed");}
} else {System.out.println("File already exists");
}if (file.delete()){System.out.println("File deleted");
}
File*的IO
// Demo1: 基本fos/fis 和 fw/fr
FileInputStream fis = new FileInputStream("data.txt")
int content ;
while ((content = fis.read()) != -1){System.out.print((char) content);
}FileOutputStream fos = new FileOutputStream("data.txt")
// 字节流fos.write 只接受bytes[] 和 int。所以其他的要先转化为bytes
fos.write("Hello World".getBytes()); // 会清空之前的文件内容FileReader fr = new FileReader("data.txt") // 和fis一模一样
int content;
while ((content = fr.read()) != -1){System.out.print((char)content);
}FileWriter fw = new FileWriter("data.txt")
// 字符流fw.write 可以接受多种,
fw.write("Hello World!");// Demo2: 文本拷贝
try (FileInputStream fis = new FileInputStream("data.txt");FileOutputStream fos = new FileOutputStream("destination.txt")){// int content ; 等效下面// while ((content = fis.read()) != -1){// fos.write((char) content);// }byte[] buff = new byte[1024];int length;while ((length = fis.read(buff)) != -1){ // BUffer*也可以使用此fos.write(buff, 0 , length); // 打印:new String(buff, 0, length)}
}
javafx
javaFX : Foundation Classes Extension
-
由于awt和swing过时
-
JavaFX 支持 CSS 和 FXML
基本Demo
/*
start() 是 JavaFX 应用程序的主要逻辑入口,用于设置 UI。
launch(args) 是 JavaFX 框架中用于启动应用程序的静态方法。并初始化 JavaFX 的运行环境、设置事件调度线程(JavaFX 应用主线程),并最终调用 start() 方法。
args即便未用到,也是一种规范的调用方式。
*/public class JavaFXExample extends Application {@Overridepublic void start(Stage primaryStage) {// 设置标题primaryStage.setTitle("JavaFX 示例");// 创建标签按钮。 注意都是javafx下,而不是awt下Label label = new Label("Hello, JavaFX!");Button button = new Button("点击我");button.setOnAction(event -> label.setText("按钮被点击了!"));// 使用 VBox 布局,其实就是容器,用于放控件VBox vbox = new VBox(10); // 布局间距为 10 像素vbox.getChildren().addAll(label, button);// 创建场景 (Scene)用于展示Scene scene = new Scene(vbox, 300, 200); // 宽 300 高 200primaryStage.setScene(scene);// 显示窗口primaryStage.show();}public static void main(String[] args) {launch(args); // 启动 JavaFX 应用程序}
}
登录Demo
primaryStage.setTitle("登录窗口");// 创建布局
GridPane gridPane = new GridPane();
gridPane.setHgap(10); // 设置水平间距
gridPane.setVgap(10); // 设置垂直间距// 添加控件
Label usernameLabel = new Label("用户名:");
TextField usernameField = new TextField();Label passwordLabel = new Label("密码:");
PasswordField passwordField = new PasswordField();Button loginButton = new Button("登录");
Label messageLabel = new Label();// 登录按钮事件
loginButton.setOnAction(event -> {String username = usernameField.getText();String password = passwordField.getText();if ("admin".equals(username) && "123".equals(password)) {messageLabel.setText("登录成功!");} else {messageLabel.setText("用户名或密码错误!");}
});// 将控件添加到布局中
gridPane.add(usernameLabel, 0, 0);
gridPane.add(usernameField, 1, 0);
gridPane.add(passwordLabel, 0, 1);
gridPane.add(passwordField, 1, 1);
gridPane.add(loginButton, 1, 2);
gridPane.add(messageLabel, 1, 3);// 创建场景
Scene scene = new Scene(gridPane, 300, 400);
primaryStage.setScene(scene);
primaryStage.show();
java.net
URL
// Demo: 通过url访问网页内容
URL url = new URL("http://example.com");
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
String s;
while ((s = br.readLine()) != null){System.out.println(s);
}
br.close();
HttpCookie
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);HttpCookie httpCookie = new HttpCookie("username", "adair");
httpCookie.setDomain("example.com");
httpCookie.setPath("/");// 添加cookie到CookieManager
URI uri = new URI("http://example.com");
cookieManager.getCookieStore().add(uri, httpCookie);// 输出cookie
System.out.println(cookieManager.getCookieStore().get(uri));
// 输出:[username="adair";$Path="/";$Domain="example.com"]
范围修饰符
访问范围
顶级类必须是 public
或者包级 default
修饰符 | 类 | 方法 | 变量 |
---|---|---|---|
public | 任何地方` | 任何地方` | 任何地方 |
protected | 只能修饰嵌套类 | 同包内、子类中可以访问 | 同包内、子类中可以访问 |
default | 只能在同包内访问 | 只能在同包内访问 | 只能在同包内访问 |
private | 不能修饰顶级类 | 只能在当前类内访问 | 只能在当前类内访问 |
// public
public class MyClass {public void myMethod() {System.out.println("This is a public method.");}// protected方法protected void myMethod() {System.out.println("This is a protected method.");}// privateprivate void myMethod() {System.out.println("This is a private method.");}
}// default,但是可以写default关键字
class MyClass { // 默认访问权限void myMethod() {System.out.println("This is a default method.");}
}
泛型
泛型不可变:是指List和 List 没有任何关系,也没继承关系。
// Demo1 : 泛型类,
public class Box<T> { // 可以传递多个泛型, <T, R>private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;}
}Box<String> stringBox = new Box<>(); // 可以String,也可以是Integer
泛型方法
// 返回值前面加上<T>
public static <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}
}String[] names = {"Alice", "Bob"};
Integer[] numbers = {1, 2, 3};printArray(names); // 推断 T 为 String
printArray(numbers); // 推断 T 为 Integer
有界泛型
// T 只能是 Number 或其子类,比如 Integer 或 Double。
class Box<T extends Number> {private T value;
}// 下界是 Integer,即列表可以接受 Integer 或其父类(如 Number)
public static void addNumber(List<? super Integer> list) {list.add(42);
}/*T extend ClassA 表示ClassA的子类? super ClassA 表示ClassA的父类
*/
有界泛型例子demo1:如何使用
//
class Animal {String name;Animal(String name) {this.name = name;}
}
// 实现的Comparable<Animal>,并非Comparable<Dog>,因为只要父类可以比较,子类就可以找其父进行比较
class Dog extends Animal implements Comparable<Animal> {Dog(String name) {super(name);}@Overridepublic int compareTo(Animal o) {return name.compareTo(o.name); // name继承父类的name}@Overridepublic String toString() {return name;}
}public class Main {/*U extends Comparable 表示 U 必须实现 Comparable 接口。<? super U> 限定了 Comparable 接口对象,表示 U 或 U 的父类(更通用)。<U extends Comparable<U>> 则表示只接受U类型,并且U实现排序。<U extends Comparable<? super U>> 表示U的父类实现Comparable即可*/public static <U extends Comparable<? super U>> void sortList(List<U> list) {list.sort(Comparator.naturalOrder()); // 使用自然排序}public static void main(String[] args) {// 只能定义为Dog,不能Animal,因为父类压根没实现ComparableList<Dog> dogs = Arrays.asList(new Dog("Charlie"), new Dog("Buddy"), new Dog("Max"));sortList(dogs); // 排序方法被成功调用System.out.println(dogs); // 输出结果按名字排序:Buddy, Charlie, Max}
}
有界泛型例子demo1:
public class Main {public static <U extends Comparable<? super U>> void sortList(List<U> list) {list.sort(Comparator.naturalOrder()); // 使用自然排序}public static void main(String[] args) throws Exception {ArrayList<Animal> list = new ArrayList<>();// 添加Dog 和 Catlist.add(new Dog("Bob"));list.add(new Cat("Alice"));sortList(list);System.out.println(list);}}class Dog extends Animal{public Dog(String name) {super(name);}
}class Cat extends Animal{public Cat(String name) {super(name);}
}class Animal implements Comparable<Animal>{String name;public Animal(String name) {this.name = name;}@Overridepublic int compareTo(Animal o) {return name.compareTo(o.name);}@Overridepublic String toString() {return name;}
}
经典例子
序列化
作用:
- 常用于存储:比如持久化(存在文件或者数据库),远程通信RMI,缓存等
transient
和static
字段不会被序列化。比如密码,使用transient
关键字避免暴露
public class Main {public static void main(String[] args) throws Exception {Person adair = new Person("Adair", 18, "123456");ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("demo.txt")));oos.writeObject(adair);System.out.println(adair); // 会输出密码ObjectInputStream ois = new ObjectInputStream(new FileInputStream("demo.txt"));Person person = (Person)ois.readObject();System.out.println(person); // 密码 passworld = null}
}class Person implements Serializable { // 仅仅标记Serializable,类似于Cloneable// 用于查看版本private static final long serialVersionID = 2L;String name;int age;transient String password;public Person(String name, int age, String password) {this.name = name;this.age = age;this.password = password;}@Overridepublic String toString() {return "name = " + name + ", age = " + age + ", password = " + password;}
}
拓展:如果序列化之后可以让人读的话,可以使用JSON
/*implementation("com.google.code.gson:gson:2.10.1") 引入json库Person不用接口Serializable
*/
Person adair = new Person("Adair", 18, "123456");
Gson gson = new Gson();
String json = gson.toJson(adair);BufferedWriter bw = new BufferedWriter(new FileWriter("demo.txt"));
bw.write(json);
bw.flush(); // 确保数据写入文件,一定要做
System.out.println(json); // {"name":"Adair","age":18}。没有密码BufferedReader br = new BufferedReader(new FileReader("demo.txt"));
System.out.println(br.readLine()); // 同上br.close(); // 流的类,都要进行关闭
bw.close();
线程:生产者消费者
public class Demo2 {public static void main(String[] args) {SharedBuffer sharedBuffer = new SharedBuffer();// 同一个sharedBuffer, 生产者和消费者两个线程进行执行new Thread(new Producer(sharedBuffer)).start();new Thread(new Consumer(sharedBuffer)).start();}
}class SharedBuffer {private boolean avalible = false; // 标志位,表示是否有产品// 生产产品,public synchronized void produce(int id) throws InterruptedException {while (avalible){ // 等待至消费者消费结束wait(); // 即this.wait(); 线程等待;释放锁(资源),其他线程可以获取该锁。}System.out.println("produce: " + id);avalible = true; // 标记产品可用notify(); // 随机唤醒一个线程,此处必然是唤醒消费者的wait();}// 消费商品public synchronized void consume(int id) throws InterruptedException {while (!avalible){wait();}System.out.println("consume: " + id);avalible = false;notify();}
}class Producer implements Runnable{SharedBuffer sharedBuffer;public Producer(SharedBuffer sharedBuffer) {this.sharedBuffer = sharedBuffer;}@Overridepublic void run() {try {for (int i = 0; i < 5; i++) {sharedBuffer.produce(i);Thread.sleep(500); // 模拟生产过程}} catch (InterruptedException e) {throw new RuntimeException(e);}}
}class Consumer implements Runnable{SharedBuffer sharedBuffer;public Consumer(SharedBuffer sharedBuffer) {this.sharedBuffer = sharedBuffer;}@Overridepublic void run() {try {for (int i = 0; i < 5; i++) {sharedBuffer.consume(i);Thread.sleep(500);}} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
Comparator:对象排序
List<Person> list = Arrays.asList(new Person("Alice", 25),new Person("Bob", 30),new Person("Alice", 20)
);
Comparator<Person> personComparator = Comparator.comparing((Person p) -> p.name).thenComparing((Person p) -> p.age);
list.sort(personComparator);
System.out.println(list);// 源码:参数为Function的lambda,返回为Comparator的lambda
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor)
{Objects.requireNonNull(keyExtractor);return (Comparator<T> & Serializable)(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
// 返回一个Comparator<T>类型的比较器,
// 值为(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));此为lambda表达式
// 并且需要强转为Comparator<T> & Serializable
// Serializable接口表示可以序列化
单例和多例
单例
- 确保一个类只有一个实例,提供统一的访问方式(通常是一个静态方法)
- 应用:日志、数据库连接池、配置文件管理。
饿汉式和懒加载(懒汉式)
public class Singleton {// private方式被newprivate Singleton() {}// 懒加载:节省资源private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}public class Singleton {private Singleton() {}// 饿汉式,只要加载类就可以private static final Singleton INSTANCE = new Singleton();public static Singleton getInstance() {return INSTANCE;}
}
多例
- 允许一个类拥有多个实例,但限制实例数量,每个实例可以通过特定的标识符获取(比如键值)。
- 应用:数据库分区管理、缓存对象
// 多例模式的实现
class Multiton {private static final Map<String, Multiton> instances = new HashMap<>();private final String name;private Multiton(String name) { // 私有构造方法,防止被newthis.name = name;}// 获取实例的方法public static Multiton getInstance(String key) {instances.putIfAbsent(key, new Multiton(key)); // 每个 key 只会创建一次return instances.get(key);}}// 测试类
public class Main {public static void main(String[] args) {Multiton instance1 = Multiton.getInstance("DB1");Multiton instance2 = Multiton.getInstance("DB2");Multiton instance3 = Multiton.getInstance("DB1"); // 获取已存在的实例,等instance1}
}
标签
outer: for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {// 表示外层breakif (i == 1) break outer; System.out.println(i + " " +j); // 0 0、0 1、0 2}
}outer: for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {// 外层continueif (i == 1) continue outer; // 0 0、0 1、0 2、2 0、2 1、2 2System.out.println(i + " " +j); }
}
枚举
class FreshJuice{enum FreshJuiceSize{ SMALL, MEDIUM, LARGE};FreshJuiceSize size; // 自定义的数据类型
}public class EumExample{public static void main(String[] args){FreshJuice juice = new FreshJuice();juice.size = FreshJuice.FreshJuiceSize.MEDIUM;System.out.println(juice.size);}
}
内部类
成员内部类、静态内部类、局部内部类、匿名内部类
// 成员内部类
public class OuterClass {pulic class InnerClass{public void display{sout("hello");}}
}
InnerClass example = new OuterClass().new InnerClass();
example.display();// 静态内部类,就是改为 pulic static class InnerClass
InnerClass example = new InnerClass();
example.display();// 局部内部类:就是方法里面定义的类,然后此方法使用这个类// 匿名内部类:new Interface()然后实现方法。这个实现接口且没有名字的实例,就叫匿名内部类。
instanceof
public class Test {public static void main(String[] args) {Object os = new Student();System.out.println(os instanceof Object);//1System.out.println(os instanceof Person);//1System.out.println(os instanceof Student);//1System.out.println(os instanceof Teacher);//0System.out.println(os instanceof String);//0Person ps = new Student();System.out.println(ps instanceof Object);//1System.out.println(ps instanceof Person);//1System.out.println(ps instanceof Student);//1System.out.println(ps instanceof Teacher);//0
// System.out.println(ps instanceof String); //报错Student ss = new Student();System.out.println(ss instanceof Object);//1System.out.println(ss instanceof Person);//1System.out.println(ss instanceof Student);//1
// System.out.println(ss instanceof Teacher); //报错
// System.out.println(ss instanceof String); //报错}
}
BigDecimal
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("0.9");
BigDecimal subtract = bd1.subtract(bd2);//add,multiply,divide
System.out.println(subtract); //0.1BigDecimal divide = new BigDecimal("1.4")
.subtract(new BigDecimal("0.5"))
.divide(new BigDecimal("0.9"));
System.out.println(divide); //1//除不尽,如果不四舍五入的话,报错
BigDecimal divide1 = new BigDecimal("10").divide(new BigDecimal("3"), 2, BigDecimal.ROUND_HALF_UP);
System.out.println(divide1); //3.33
assert
用于调试使用,如果不符合条件运行直接异常。需要在VM options中增加-ea(enable assert)。
// assert
public class Demo2 {public static void main(String[] args) {// 设置初始值String s = null;// 判断s值是否为空,并用assert赋值boolean。结果为false。assert s==null?false:true;System.out.println("end");}
}
public class VolatileTest {// 如果flag没有volatile,则Thread1卡在while中volatile boolean flag = true;public void updateFlag() {this.flag = false;System.out.println("修改flag值为:" + this.flag);}public static void main(String[] args) {VolatileTest test = new VolatileTest();new Thread(() -> {while (test.flag) { }System.out.println(Thread.currentThread().getName() + "结束");}, "Thread1").start();new Thread(() -> {try {Thread.sleep(2000);test.updateFlag();} catch (InterruptedException e) {}}, "Thread2").start();}
}
测试单元
import org.junit.Test;
import static org.junit.Assert.*;public class ExampleUnitTest {@Test // 表示这是一个测试例子,可以单独运行public void addition_isCorrect() {// 测试一个简单的加法函数assertEquals(4, 2 + 2);}// 测试是否有相应的异常@Test(expected = ArithmeticException.class)public void division_byZero() {int result = 1 / 0;}
}
测试单元:
- 必须是 public 修饰的。
- 必须是 void 返回类型。
- 不能有参数。
其他
输出解释
// 在直接输出对象(即toString()),Object[][][]输出[[[Ljava.lang.Object;
// L表示对象的引用,后面表示Hash值
[Ljava.lang.Object@74a14482;// 比如 [I 表示int[]。 [Ljava.lang.String;表示String[]
字符串
// 正则表达式。这个用于判定纯数字
str.matches("\\d+")
常用方法:
LocalDateTime.now(); // 获取当前时间
Thread.currentThread().getName(); // 当前
有待解决
strictfp:用于修饰接口、类、方法用于浮点数的精确计算。strict interface MyInterface/class MyClass/int myMethod。不能用于修饰接口的方法。
new Student();
System.out.println(ss instanceof Object);//1
System.out.println(ss instanceof Person);//1
System.out.println(ss instanceof Student);//1
// System.out.println(ss instanceof Teacher); //报错
// System.out.println(ss instanceof String); //报错
}
}
## BigDecimal```javaBigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("0.9");
BigDecimal subtract = bd1.subtract(bd2);//add,multiply,divide
System.out.println(subtract); //0.1BigDecimal divide = new BigDecimal("1.4")
.subtract(new BigDecimal("0.5"))
.divide(new BigDecimal("0.9"));
System.out.println(divide); //1//除不尽,如果不四舍五入的话,报错
BigDecimal divide1 = new BigDecimal("10").divide(new BigDecimal("3"), 2, BigDecimal.ROUND_HALF_UP);
System.out.println(divide1); //3.33
assert
用于调试使用,如果不符合条件运行直接异常。需要在VM options中增加-ea(enable assert)。
// assert
public class Demo2 {public static void main(String[] args) {// 设置初始值String s = null;// 判断s值是否为空,并用assert赋值boolean。结果为false。assert s==null?false:true;System.out.println("end");}
}
public class VolatileTest {// 如果flag没有volatile,则Thread1卡在while中volatile boolean flag = true;public void updateFlag() {this.flag = false;System.out.println("修改flag值为:" + this.flag);}public static void main(String[] args) {VolatileTest test = new VolatileTest();new Thread(() -> {while (test.flag) { }System.out.println(Thread.currentThread().getName() + "结束");}, "Thread1").start();new Thread(() -> {try {Thread.sleep(2000);test.updateFlag();} catch (InterruptedException e) {}}, "Thread2").start();}
}
测试单元
import org.junit.Test;
import static org.junit.Assert.*;public class ExampleUnitTest {@Test // 表示这是一个测试例子,可以单独运行public void addition_isCorrect() {// 测试一个简单的加法函数assertEquals(4, 2 + 2);}// 测试是否有相应的异常@Test(expected = ArithmeticException.class)public void division_byZero() {int result = 1 / 0;}
}
测试单元:
- 必须是 public 修饰的。
- 必须是 void 返回类型。
- 不能有参数。
其他
输出解释
// 在直接输出对象(即toString()),Object[][][]输出[[[Ljava.lang.Object;
// L表示对象的引用,后面表示Hash值
[Ljava.lang.Object@74a14482;// 比如 [I 表示int[]。 [Ljava.lang.String;表示String[]
字符串
// 正则表达式。这个用于判定纯数字
str.matches("\\d+")
常用方法:
LocalDateTime.now(); // 获取当前时间
Thread.currentThread().getName(); // 当前
有待解决
strictfp:用于修饰接口、类、方法用于浮点数的精确计算。strict interface MyInterface/class MyClass/int myMethod。不能用于修饰接口的方法。