前端作业js基础
js中var和let的差异
作用域差异:var声明的变量具有函数作用域或全局作用域;let声明的变量具有块作用域,只能在包含它的代码块内部访问。
变量提升行为:var声明的变量会发生变量提升,在代码执行之前,变量的声明会被提升到作用域的顶部,但赋值不会提升;let声明的变量不会提升,在声明前访问会抛出错误。
重复声明:同一作用域中,var允许重复声明同一个变量,而let不允许。
js中的函数定义方式
1/ 函数声明:function函数名(参数){//函数体}
2/ 函数表达式:let 变量名=function(参数){//函数体}
3/ 箭头函数:let 变量名=(参数)=>{//函数体}
4/ 函数构造器:let 函数名=new function('参数1','参数2','函数体');
5/ 方法简写:let 对象={方法名(参数){//函数体}}
匿名函数与普通函数的区别
1>命名:普通函数有名称,匿名函数没有
2>引用:普通函数可通过名称直接调用,匿名函数需赋值给变量或作为参数传递
3>用途:匿名函数常用于回调、IIFE及事件处理
4>堆栈跟踪:普通函数在错误堆栈中显示函数名,匿名函数更难调试
箭头函数
1>语法简洁:单参数可省略括号,单语句可省略return和{}
2>无this绑定:继承上下文this,没有自己的this
3>不能当构造函数:不能用new调用
4>没有arguments对象:使用剩余参数(..args)代替
5>无prototype属性:不能用于继承
6>不能使用yield:不能作为生成器函数
js中的变量类型
1/基本数据类型:number、string、Boolean、null、undefined、symbol、bigint
2/引用数据类型:object、array、function、date、regexp等
拓展
变量赋值:发生在基本数据类型赋值时
引用赋值:发生在引用类型赋值时
函数在js中特殊的对象,属于引用类型,可以赋值给变量,可以作为参数传递,可以作为返回值。可以像其他变量一样使用,但赋值时遵循引用赋值的规则
for、foreach、for in、for of
for:最基础的循环方式;可以精确控制循环的起始和步长;可以使用break/continue;适用于需要使用索引的场景
for(let i=0;i<10;i++){}
foreach: 更现代的函数式写法;不能使用break/continue;不能提前终止循环;适用于需要遍历数组每个元素的场景
const array=[1,2,3];
array.foreach((item,index,array)=>{})
for in: 主要用于遍历对象的可枚举属性;会遍历原型链上的属性;不适合遍历数组(因为会遍历数组的其他属性);可以使用break/continue
const obj={a:1,b:2,c:3};
for(let key in obj){}
for of: ES6新增的遍历方式;可以遍历所有可迭代对象(Array, Map, Set, String等);直接获取值而不是索引;可以使用break/continue;不能遍历普通对象
const array=[1,2,3];
for(let value of array){}
拓展
迭代器和生成器:迭代器时一个对象,定义了一个序列,终止时可能返回一个返回值
生成器时一种特殊的函数,可以通过yield关键字暂停和恢复执行
json转换为字符串
// 使用Jackson库
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(object);
// 使用Gson库
Gson gson = new Gson();
String jsonString = gson.toJson(object);
字符串转对象
// 使用Jackson库
ObjectMapper mapper = new ObjectMapper();
MyClass obj = mapper.readValue(jsonString, MyClass.class);
// 使用Gson库
Gson gson = new Gson();
MyClass obj = gson.fromJson(jsonString, MyClass.class);
object、{}和Map的区别
Object(Java)
是所有Java类的根类
包含基础方法如equals()、hashCode()等
可以存储任何类型的对象
{}(JavaScript)
是JavaScript中的对象字面量表示法
是一个动态的键值对集合
可以随时添加或删除属性
属性可以是任何类型
Map(Java)
是一个接口,定义了键值对的集合
有多种实现如HashMap、TreeMap等
提供了特定的方法操作键值对
可以指定键和值的类型
JavaScript对象属性顺序
JavaScript对象的属性顺序遵循以下规则
数字键:
首先按照数值大小升序排列
字符串键:
按照属性被添加的顺序排列
ES2015+规范保证了这个顺序
属性顺序会在以下情况发生改变
删除属性后重新添加
对象被序列化后再解析
使用Object.assign()等方法合并对象
通过不同方式(如Proxy)访问对象
后端作业 java语法基础
java变量的基本类型
整数类型:byte,short,int,long
浮点类型:float,double
字符类型:char
布尔类型:boolean
封装类型和基本类型的关系
byte -> Byte
short -> Short
int -> Integer
long -> Long
float -> Float
double -> Double
char -> Character
boolean -> Boolean
进阶
基本数字类型转换
向下转换(大类型转小类型):
- 可能会发生数据截断
- 遵循补码规则
- 截取低位字节
- 可能导致精度损失
向上转换(小类型转大类型):
- 自动进行,无需显式转换
- 不会发生数据丢失
- 高位补符号位
封装类型转换
当基本类型和包装类型互相转换时
- 自动装箱实际调用Integer.valueOf()
- 自动拆箱实际调用intValue()
对象创建方式的区别
Integer的创建:
区别:
- new Integer()始终创建新对象
- 自动装箱会使用缓存池(-128到127之间的值)
String的创建:
区别:
- new String()会在堆内存创建新对象
- 直接赋值会先检查字符串常量池,有则复用,无则创建
缓存池使用规则
Integer缓存池:
- 范围:-128到127
- 通过Integer.valueOf()方法获取
- 超出范围会创建新对象
String常量池:
- 字符串字面量直接使用常量池
- intern()方法可以将堆上的字符串对象添加到常量池
Java对象实例的生成方式
使用new关键字(最常用)
Student student = new Student();
使用Class类的newInstance()方法
Student student = Student.class.newInstance();
使用Constructor类的newInstance()方法
Constructor<Student> constructor = Student.class.getConstructor();
Student student = constructor.newInstance();
使用Clone方法
Student student2 = (Student) student1.clone();
使用反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("student.obj"));
Student student = (Student)in.readObject();
对象的回收
Java对象的回收由垃圾收集器(Garbage Collector,GC)自动完成。当对象变成不可达(unreachable)时,就可能被回收。对象变成不可达的情况包括:
- 对象没有被任何引用指向
- 对象的引用超出了作用域
- 引用被显式设置为null
四种引用类型
1) 强引用(Strong Reference)
Object obj = new Object();
特点:
- 最常见的引用类型
- 只要强引用存在,垃圾收集器永远不会回收被引用的对象
- 即使内存不足,JVM也不会回收,而是抛出OutOfMemoryError
2) 软引用(Soft Reference)
SoftReference<Object> softRef = new SoftReference<Object>(new Object());
特点:
- 内存充足时不会被回收
- 内存不足时会被回收
- 常用于实现内存敏感的高速缓存
3) 弱引用(Weak Reference)
WeakReference<Object> weakRef = new WeakReference<Object>(new Object());
特点:
- 下一次垃圾收集发生时,无论内存是否充足,都会被回收
- 常用于避免内存泄漏,如WeakHashMap
4) 虚引用(Phantom Reference)
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<Object>(new Object(), queue);
特点:
- 最弱的引用关系
- 随时可能被回收
- 必须和ReferenceQueue一起使用
- 主要用于跟踪对象被垃圾回收的状态
Java中的遍历方式
for循环(普通for循环)
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
特点:
- 最基础的循环方式
- 可以控制循环的起始位置和步长
- 可以同时遍历多个数组
- 适合需要使用索引的场景
增强for循环(for-each)
for (String item : list) {
System.out.println(item);
}
特点:
- 语法简洁,代码可读性强
- 不能获取索引位置
- 不能修改集合中的元素
- 适合只读遍历场景
while循环
int i = 0;
while (i < list.size()) {
System.out.println(list.get(i));
i++;
}
特点:
- 适合不知道具体循环次数的场景
- 可以灵活控制循环条件
- 需要手动控制索引递增
Iterator迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
}
特点:
- 可以在遍历过程中安全地删除元素
- 只能单向遍历
- 提供了统一的遍历接口
迭代器(Iterator)
迭代器是一种设计模式,提供了一种方法来访问集合中的元素,而不需要暴露其底层的表示方式。
主要方法:
- hasNext(): 判断集合中是否还有元素
- next(): 获取下一个元素
- remove(): 删除当前元素
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class IteratorExample { public static void main(String[] args) { // 创建ArrayList集合 List<String> fruits = new ArrayList<>(); fruits.add("苹果"); fruits.add("香蕉"); fruits.add("橙子"); // 使用Iterator遍历并删除特定元素 Iterator<String> iterator = fruits.iterator(); while (iterator.hasNext()) { String fruit = iterator.next(); if (fruit.equals("香蕉")) { iterator.remove(); // 安全地删除元素 } else { System.out.println(fruit); } } } }
static,const,final
static(静态)
- 表示某个成员属于类本身,而不是类的实例
- 可以不创建对象实例就能访问
- 被所有实例共享
可以使用的位置
类成员变量
class Example {
static count = 0;
}
类方法
class Example {
static sayHello() {
console.log('Hello');
}
}
类块内部
class Example {
static {
// 静态初始化块
}
}
const(常量)
- 声明一个不可重新赋值的变量
- 值不可变(对于基本类型),但对象的属性可以修改
- 必须在声明时初始化
可以使用的位置
变量声明
const PI = 3.14159;
函数参数
function process(const param) {}
类中的只读属性(某些语言支持)
class Example {
readonly constant = 42; // TypeScript中的写法
}
final(最终)
- 在Java等语言中使用,JavaScript没有此关键字
- 表示某个实体不能被修改或继承
- 类似于const和readonly的组合
可以使用的位置
类声明
final class Example {
// 此类不能被继承
}
方法声明
class Example {
final void method() {
// 此方法不能被重写
}
}
变量声明
final int constant = 100;
if else,switch case,三目运算符
if-else 语句
- 按顺序执行条件判断,直到找到第一个为真的条件
- 每个条件都是独立判断,不存在跳转表
- 当条件较多时,时间复杂度为 O(n)
- 编译器可能会对简单的 if-else 进行优化
switch-case 语句
- 编译器通常会将 switch-case 优化成跳转表(jump table)或查找表
- 跳转表使得执行时间与 case 数量无关,时间复杂度为 O(1)
- 但需要额外的内存来存储跳转表
- 只适用于整型值(包括枚举)的比较
- 当 case 值不连续或差距较大时,可能会退化成 if-else 结构
三目运算符(?:)
- 通常编译成单个条件跳转指令
- 只能处理二元条件(真/假)
- 由于结构简单,编译器容易优化
- 在字节码层面通常比 if-else 更紧凑
equals()是用什么进行比较的,两字符串为什么用 == 无法比较。
equals() 方法比较原理
- equals() 默认实现(来自Object类)是使用 == 进行比较,比较的是对象的引用(内存地址)
- 很多类(如String、Integer等)都重写了equals()方法,用于比较对象的内容而不是引用
- 例如String类的equals()重写后会逐个比较字符串中的字符
为什么字符串不能用 == 比较
- == 运算符比较的是对象的引用(内存地址),而不是字符串的内容
- 当使用字符串字面量时,Java会将其存入字符串常量池
- 使用new创建的字符串会在堆内存中创建新对象
- 所以即使两个字符串内容相同,它们的引用可能不同
hashcode()
hashCode()的默认返回值
默认的hashCode()方法返回的是对象在JVM中的内存地址转换成的一个整数。这个值是由JVM根据对象的内存地址计算得出的,但具体的计算方式可能因不同的JVM实现而异。
重写hashCode()会影响的函数
- HashMap的put/get/remove等操作
- HashSet的add/remove/contains等操作
- Hashtable的相关操作
- 任何依赖hashCode()的缓存实现
HashMap和HashSet
HashMap的判断机制
首先使用hashCode()计算对象的哈希值
根据哈希值确定对象在内部数组中的存储位置
如果该位置已经有元素:
- 先比较hashCode值是否相等
- 如果hashCode相等,再调用equals()方法进行比较
- 只有当hashCode相等且equals()返回true时,才认为是同一个对象
HashSet的判断机制
HashSet内部实际上是使用HashMap来实现的
当我们使用HashSet时,实际上是将元素作为HashMap的key来存储
判断重复的逻辑与HashMap完全相同:
- 先比较hashCode()
- 如果hashCode相等,再比较equals()
- 两者都相等才认为对象是重复的
Java序列化和反序列化基础
- 序列化(Serialization):将Java对象转换为字节序列的过程
- 反序列化(Deserialization):将字节序列恢复为Java对象的过程
实现接口
- Java使用Serializable接口来实现序列化
- 这是一个标记接口(marker interface),不需要实现任何方法
Fastjson序列化与反序列化Map
Map转JSON字符串
// Map转JSON
Map<String, Object> map = new HashMap<>();
map.put("name", "张三");
map.put("age", 25);
// 序列化
String jsonString = JSON.toJSONString(map);
JSON字符串转Map
// JSON转Map
String jsonString = "{\"name\":\"张三\",\"age\":25}";
// 反序列化
Map<String, Object> map = JSON.parseObject(jsonString, Map.class);
Fastjson的工作原理
序列化过程
-
使用反射获取对象的属性信息
-
遍历Map的键值对
-
将每个键值对转换为JSON的key-value形式
-
处理特殊类型(如日期、数字等)
反序列化过程
-
解析JSON字符串为词法单元
- 根据目标类型(Map)创建对象
- 解析键值对并填充到Map中
- 自动类型转换(如String到Integer等)