随机数 在java中有常见的两种生成随机数的方法是Math.random() 和java.util.Random类
基本用法
Math.random() 方法 功能: 生成[0.0,1.0)范围内的double类型的伪随机数,线程安全的
语法:Math.ranndom()
1 2 3 4 5 6 7 8 9 int randomInt = (int )(Math.random() * 100 ) int randomInt = (int )(Math.random() * 30 ) + 1 int min = 5 , max = 20 ;int randomInt = (int ) (Math.random * (max - min + 1 )) + min
java.util.Random类 基本用法
功能 :生成多种类型的随机数(整数、浮点数、布尔值等)。线程不安全
语法 :
1 2 Random random = new Random (); Random seededRandom = new Random (123L );
核心方法
方法
返回值
范围
nextInt()
int
所有可能的 int 值
nextInt(int bound)
int
[0, bound)
nextDouble()
double
[0.0, 1.0)
nextBoolean()
boolean
true 或 false
nextLong()
long
所有可能的 long 值
函数的可变形参 声明方式: function test(int a, int b, 类型 ….参数名)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import java.util.Arrays;public class Main { public static void main (String[] args) { test(1 ,2 ,3 ,4 ,5 ); } public static void test (int a, int b, int ...c) { for (int i = 0 ; i < c.length; i++) { System.out.print(c[i] + " " ); } } }
关键字 package import package package,称为包,用于指明该文件中定义的类、接口等结构所在的包。
语法格式:package 顶层包名.子包名 ;
举例:pack1\pack2\PackageTest.java
1 2 3 4 5 6 7 package pack1.pack2; public class PackageTest { public void display () { System.out.println("in method display()" ); } }
说明:
• 一个源文件只能有一个声明包的package语句
• package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。
• 包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意 – – 包通常使用所在公司域名的倒置:com.atguigu.xxx。 大家取包名时不要使用”java.xx”包
• 包对应于文件系统的目录,package语句中用 “.” 来指明包(目录)的层次,每.一次就 表示一层文件目录。
• 同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。 不同的包下可以定义同名的结构(类、接口) 8.1.2 包的作用
• 包可以包含类和子包,划分项目层次,便于管理
• 帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模 式
• 解决类命名冲突的问题 • 控制访问权限
import 为了使用定义在其它包中的Java类,需用import语句来显式引入指定包下所 需要的类。相当于import语句告诉编译器到哪里去寻找这个类。
1 2 3 4 5 6 7 import pack1.pack2.Test; //import pack1.pack2.*;表示引入pack1.pack2 包中的所有结构 public class PackTest{ public static void main(String args[]){ Test t = new Test(); t.display(); } }
instanceof 关键字 判断某个对象是否属于某个类
1 a对象 instalof A类 如果a对象是A类型的子类 则是true 反之false
getClass() 获取运行时类 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.test.www.utils;public class MyArrays { public static void main (String[] args) { Person person = new Person (); System.out.println(person.getClass()); } } class Person {}
interface 接口 接口是什么?怎么使用?
比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public interface Animal { void eat () ; void sleep () ; } public class Dog implements Animal { @Override public void eat () { System.out.println("狗吃骨头" ); } @Override public void sleep () { System.out.println("狗趴着睡觉" ); } }
接口可以定义什么?
抽象方法
默认方法
静态方法
私有方法(用于默认方法或静态方法内部)
常量
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface ExampleInterface { void abstractMethod () ; default void defaultMethod () { System.out.println("这是一个默认方法。" ); helperMethod(); } static void staticMethod () { System.out.println("这是一个静态方法。" ); } private void helperMethod () { System.out.println("这是一个私有的辅助方法。" ); } }
什么时候用接口? 什么时候用抽象类?
如果一组类需要保证某种行为的一致,就用接口 就比如 飞机可以射击, 手枪可以射击, 但是它们两个就不属于同一类
再比如:所有支付方式(支付宝、微信、银行卡)都应该有 pay() 方法,不管内部逻辑不同。
抽象类是”是什么”的体现,如果想体现一种继承关系就用抽象类 比如 动物 老虎, 狮子, 狗 都属于动物, 它们是同类关系. 也都可以相同的方法, 比如吃, 走, 叫.玩.
接口冲突 一个类实现了多个接口,而这些接口中定义了相同名字的默认方法,常量
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 com.test.www.utils;interface A { int a = 10 ; default void show () { System.out.println("接口A的show方法" ); } } interface B { int a = 20 ; default void show () { System.out.println("接口B的show方法" ); } } public class MyArrays { public static void main (String[] args) { Person person = new Person (); person.show(); person.printConst(); } } class Person implements A ,B{ @Override public void show () { A.super .show(); } public void printConst () { System.out.println(A.a); } }
内部类 什么是内部类?
内部类就定义在类中的另一个类(就像”套娃”一样)
为什么要有内部类?
让两个类之间的关系更紧密
内部类可以直接访问外部类的属性和方法(包括私有的)
有些类只服务器外部类的逻辑,不需要被外界知道
简化代码,增加封装性
内部类分类
成员内部类(类中定义)
局部内部类(方法里定义)
匿名内部类(临时创建,没有名字)
静态内部类(static 修饰 属于外部类本身)
成员内部类
定义在外部类的成员位置,没有 static 关键字。
可以直接访问外部类的所有成员变量和方法(包括private的)必须通过外部类对象才能创建内部类对象!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Outer { private String name = "外部类" ; class Inner { public void show () { System.out.println("访问:" + name); } } private void test () { Inner inner = new Inner (); inner.show(); } }
局部内部类(定义在方法内部)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Outer { void method () { class Inner { void show () { System.out.println("局部内部类的方法" ); } } Inner inner = new Inner (); inner.show(); } } public class Test { public static void main (String[] args) { Outer outer = new Outer (); outer.method(); } }
特点:
只在方法里局部有效。
访问外部变量时,如果访问的是方法里的局部变量,那么那个局部变量必须是 final(或者事实上的 final)。
(事实上的 final:就是变量一旦赋值后,后面没有再改变)
匿名内部类(超重要,尤其是配合接口)
没有名字的内部类,经常用来临时实现接口或抽象类,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface Animal { void eat () ; } public class Test { public static void main (String[] args) { Animal cat = new Animal () { @Override public void eat () { System.out.println("小猫吃鱼" ); } }; cat.eat(); } }
一边定义类,一边创建对象。
常用于简化代码,比如事件监听器、回调函数等。
静态内部类(加了 static)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Outer { static class Inner { void show () { System.out.println("静态内部类的方法" ); } } } public class Test { public static void main (String[] args) { Outer.Inner inner = new Outer .Inner(); inner.show(); } }
枚举类 什么是枚举类?
枚举就是一组固定的常量集合,放在一个类里集中管理
比如一周的七天、四季、订单状态(已下单、已发货、已签收)等等,这些都可以用枚举。
枚举可以有属性、方法、构造器 ,而且还可以像普通类那样继承接口!
在 Java 里,用 enum 关键字来定义枚举类。
简单示例:
1 2 3 public enum WeekDay { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
枚举类的基础用法
1 2 3 public enum Color { RED, GREEN, BLUE; }
使用方式:
1 2 Color c = Color.RED;System.out.println(c);
每个枚举都是一个对象(单例模式)
在 Java 中,Color.RED、Color.GREEN 这些都是 Color 类的对象,而且是唯一的(单例)。
枚举遍历(values() 方法)
1 2 3 4 5 6 7 8 for (Color color : Color.values()) { System.out.println(color); }
获取枚举名字(name() 方法)
1 System.out.println(Color.RED.name()); // 输出:RED
获取枚举的序号(ordinal() 方法)
1 2 System.out.println(Color.RED.ordinal()); // 输出:0 System.out.println(Color.GREEN.ordinal()); // 输出:1
枚举的高级用法(带属性、方法)
枚举不仅能存常量,还能存数据和行为!
比如订单状态,就可以定义不同的编码和描述:
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 public enum OrderStatus { CREATED(1 , "已创建" ), PAID(2 , "已支付" ), SHIPPED(3 , "已发货" ), RECEIVED(4 , "已收货" ); private final int code; private final String description; OrderStatus(int code, String description) { this .code = code; this .description = description; } public int getCode () { return code; } public String getDescription () { return description; } }
使用方法:
1 2 3 OrderStatus status = OrderStatus.PAID;System.out.println(status.getCode()); System.out.println(status.getDescription());
枚举还能实现接口!
枚举可以像普通类一样实现接口,比如:
1 2 3 4 5 6 7 8 9 10 11 12 interface Message { void printMessage () ; } public enum Mood implements Message { HAPPY, SAD, ANGRY; @Override public void printMessage () { System.out.println("当前心情是:" + this .name()); } }
总结:
枚举的构造方法默认是 private,不能写 public。
枚举不能直接继承其他类(因为 Java 里每个枚举类都默认继承自 java.lang.Enum)。
可以实现接口,但不能继承别的类。
可以定义抽象方法,让每个枚举对象单独实现不同的行为!(更高级)
注解 什么是注解?
用生活中的例子解释(如商品标签、书的批注)
技术定义:Java 5引入的元数据形式,用于为代码提供附加信息
与注释(comments)的区别:注解会被编译器处理,能被运行时读取
展示常见内置注解:@Override, @Deprecated, @SuppressWarnings
注解的作用?
基本语法
1 2 3 4 5 6 7 8 9 public @interface MyAnnotation { String value () default "default" ; int count () default 1 ; } @MyAnnotation(value = "test", count = 5) public class MyClass { ... }
元注解
@Target:指定注解可以应用的位置(类、方法、字段等)
TYPE: 代表类, 接口,枚举
FIELD: 属性
METHOD: 方法
PARAMETER: 形参
CONSTRUCTOR: 构造器
LOCAL_VARIABLE: 局部变量
ANNOTATION_TYPE:注解类型(表示只可以用于注解)
PACKAGE: 包
TYPE_PARAMETER: 参数类型(表示指可以用于参数类型)
@Retention:指定注解保留策略(SOURCE, CLASS, RUNTIME)
SOURCE: 保留在源码阶段,编译器编译后直接丢弃该注解信息
CLASS:保留在字节码阶段,该注解会记录在类文件中,但运行时会丢弃
RUNTIME: 保留到运行期间,即在运行期间都可以读取该注释,,程序员自定义注释都使用的这个策略
@Documented:是否包含在Javadoc中
@Inherited:是否允许子类继承
自定义注解
自定义注解的语法:
1 2 3 4 @元注解 [修饰符] @interface 注解名{ 返回值类型 方法名() default 默认返回值 }
在java中注解被看做一种特殊的接口 ,注解中可以没有任何成员, 也可以声明一个或多个抽象方法, 这里的抽象方法不能声明参数列表,返回值类型只能是8大基础类型和String, enum, class,array,泛型,其他注解类型
如果注解只有一个抽象方法, 那么建议抽象方法名为value. value名的抽象方法在使用该注解的时候可以省略value= 而是直接指定返回值
一般注解配合反射才有意义
由于java 没有带注解的检测. 所以一般在开发中常用第三方检测框架
Checker Framework 编译时检测框架
Hiberanate Validator检测框架
@Retention - 指定注解保留策略
1 2 3 4 5 6 7 8 9 10 11 12 import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME) public @interface RuntimeAnnotation {} @Retention(RetentionPolicy.SOURCE) public @interface SourceAnnotation {}
@Target - 指定注解使用目标
1 2 3 4 5 6 7 8 9 10 11 12 import java.lang.annotation.ElementType;import java.lang.annotation.Target;@Target(ElementType.METHOD) public @interface MethodAnnotation {} @Target({ElementType.TYPE, ElementType.FIELD}) public @interface TypeAndFieldAnnotation {}
@Documented - 包含在Javadoc中
1 2 3 4 5 6 7 import java.lang.annotation.Documented;@Documented public @interface DocumentedAnnotation { String value () ; }
@Inherited - 允许子类继承
1 2 3 4 5 6 7 import java.lang.annotation.Inherited;@Inherited public @interface InheritedAnnotation { String value () default "default" ; }
@Repeatable - 允许重复使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import java.lang.annotation.Repeatable;public @interface Roles { Role[] value(); } @Repeatable(Roles.class) public @interface Role { String value () ; } @Role("admin") @Role("user") public class User {}
自定义注解实战案例
REST API权限控制
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 public enum AccessLevel { PUBLIC, USER, ADMIN, SUPER_ADMIN } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AccessControl { AccessLevel requiredLevel () default AccessLevel.USER; } public class UserController { @AccessControl(requiredLevel = AccessLevel.ADMIN) public void deleteUser (String userId) { } @AccessControl(requiredLevel = AccessLevel.PUBLIC) public void getUserInfo (String userId) { } }
数据库表映射
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 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Table { String name () ; String schema () default "" ; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Column { String name () ; String type () ; boolean nullable () default true ; int length () default 255 ; } @Table(name = "employees", schema = "hr") public class Employee { @Column(name = "emp_id", type = "BIGINT", nullable = false) private Long id; @Column(name = "emp_name", type = "VARCHAR", length = 100) private String name; @Column(name = "salary", type = "DECIMAL") private BigDecimal salary; }
单元测试标记
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 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TestCategory { String[] categories(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface PerformanceTest { int expectedTime () default 100 ; } public class CalculatorTest { @Test @TestCategory(categories = {"fast", "math"}) public void testAdd () { } @Test @PerformanceTest(expectedTime = 500) @TestCategory(categories = {"slow", "integration"}) public void testComplexCalculation () { } }
日志切面标记
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 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Loggable { Level value () default Level.INFO; boolean params () default true ; boolean result () default false ; enum Level { DEBUG, INFO, WARN, ERROR } } public class OrderService { @Loggable(Level.INFO, params = true, result = true) public Order createOrder (OrderRequest request) { } @Loggable(Level.ERROR) public void cancelOrder (String orderId) { } }
简单注解处理器示例
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 import javax.annotation.processing.*;import javax.lang.model.SourceVersion;import javax.lang.model.element.TypeElement;import javax.lang.model.element.Element;import java.util.Set;@SupportedAnnotationTypes("com.example.NotNull") @SupportedSourceVersion(SourceVersion.RELEASE_17) public class NotNullProcessor extends AbstractProcessor { @Override public boolean process (Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(NotNull.class)) { processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, "@NotNull字段必须被正确初始化" , element); } return true ; } }
String类 字符串一般存储在堆/常量池中. String是不可变的序列,真正因为是不可变的序列所以JVM专门为字符串提供了一个常量池,凡是放在常量池的字符串对象都可以共享
例如
1 2 3 String s1 = "hello world" ;String s2 = "hello world" ;System.out.println(s1 == s2)
当然也不是所有的方式都放在常量池中.
1 2 3 String s1 = new String ("hello world" );String s2 = new String ("hello world" );System.out.println(s1 == s2);
上面的代码返回的是false, 说明他们指向了不同的对象
哪些字符串是放在常量池中?
直接”xxx” 得到的字符串放在常量池
直接 “xxx” + “xxx” 得到的字符串放在常量池
两个指向”xxx”常量的拼接放在常量池
所有字符串对象.intern()方法得到的结果放在常量池中
除了上面4种方式其他都放在堆中
1 2 3 4 5 6 7 8 9 10 11 12 String s1 = "hello world" ; String s2 = "hello" + "world" String s3 = s1 + s2 final String s4 = "hello " ;final String s5 = "world" ;String s6 = s4 + s5; String s7 = (s3 + s4).intertn();String s8 = s4 + "world" ; String s9 = new String ("hello world" );String s10 = String.valueof(new char []{'h' ,'e' ,'l' ,'l' ,'o' });String s11 = s1.concat(s2);
虽然有些字符串是存储在堆中,但是值还是指向了常量池
可变字符序列 StringBuffer 和 StringBuilder String类生成的对象是不可变的,如果涉及频繁的修改和拼接,则效率会很低,所以如果一个字符串需要经常更新,就考虑用可变字符序列StringBuffer和StringBuilder,它们都在java.lang包下
StringBuffer和StringBuilder的区别
StringBuffer是线程安全的, 一般涉及多线程的时候就可以用StringBuffer
StringBuilder是线程不安全的, 一般单线程的时候使用 StringBuilder要比StringBuffer的效率要高
集合 java集合是一个特别有用的工作类,可以存储数量不等到多个对象
ArrayList(最常用的列表)
场景:维护用户列表、购物车、结果列表等
优点:查询快,随机访问快
缺点:中间插入或删除效率低(要移动后面元素)
1 2 3 4 List<String> list = new ArrayList <>(); list.add("张三" ); list.add("李四" ); System.out.println(list.get(1 ));
LinkedList
场景:双端操作频繁(插入、删除) 优点:插入/删除快(特别是头尾) 缺点:查询慢,内存开销大(链式结构)
1 2 3 4 LinkedList<String> list = new LinkedList <>(); list.addFirst("头部" ); list.addLast("尾部" ); System.out.println(list.removeFirst());
HashSet
场景:需要元素去重、不关心顺序 优点:查找快,去重能力强 缺点:无序、不保证遍历顺序
1 2 3 4 5 Set<String> set = new HashSet <>(); set.add("A" ); set.add("B" ); set.add("A" ); System.out.println(set);
TreeSet
场景:需要元素自动排序 + 去重 优点:元素有序(自然排序或自定义 Comparator) 缺点:插入/查询性能比 HashSet 慢(红黑树结构)
1 2 3 4 5 Set<Integer> set = new TreeSet <>(); set.add(3 ); set.add(1 ); set.add(2 ); System.out.println(set);
LinkedHashSet
场景 :需要去重并保留插入顺序 优点 :保留顺序、无重复元素 缺点 :性能不如 HashSet,略占内存
1 2 3 4 5 Set<String> set = new LinkedHashSet <>(); set.add("a" ); set.add("b" ); set.add("a" ); System.out.println(set);
PriorityQueue
场景:需要自动排序的队列(最小值或最大值优先) 优点:每次出队都是最小(或最大)元素,适合调度器 缺点:不支持按插入顺序遍历
1 2 3 4 5 6 PriorityQueue<Integer> queue = new PriorityQueue <>(); queue.add(5 ); queue.add(1 ); queue.add(3 ); System.out.println(queue.poll());
Queue(以 LinkedList 实现)
场景:标准队列结构(先进先出) 优点:双端操作,支持 offer / poll 等队列方法 缺点:不支持线程安全,不适合并发使用
1 2 3 4 5 Queue<String> queue = new LinkedList <>(); queue.offer("A" ); queue.offer("B" ); System.out.println(queue.poll());
HashMap
场景:快速存储和查找键值对 优点:查找、插入、删除快(基于哈希) 缺点:无序,不保证遍历顺序;线程不安全
1 2 3 4 Map<String, String> map = new HashMap <>(); map.put("name" , "张三" ); map.put("age" , "25" ); System.out.println(map.get("name" ));
TreeMap
场景 :需要按 key 排序访问优点 :自动按 key 排序(自然顺序或自定义)缺点 :性能略低(红黑树实现),不适合频繁写入场景
1 2 3 4 Map<String, Integer> map = new TreeMap <>(); map.put("b" , 2 ); map.put("a" , 1 ); System.out.println(map);
LinkedHashMap
场景:既要 HashMap 的查找效率,又要保持插入顺序 优点:有序(插入顺序),支持 LRU 缓存(可重写方法) 缺点:内存开销略大,遍历速度比 HashMap 慢些
1 2 3 4 5 Map<String, String> map = new LinkedHashMap <>(); map.put("first" , "A" ); map.put("second" , "B" ); System.out.println(map);