2024-08-10

浅拷贝和深拷贝是编程中处理对象复制的两种方式。浅拷贝复制了对象的最外层,而深拷贝则递归地复制了对象的所有层级。

浅拷贝

JavaScript 中实现浅拷贝的方法有:

  1. 使用 Object.assign()
  2. 通过展开运算符 ...
  3. 手动遍历对象属性并复制



// 使用 Object.assign()
const shallowCopy = Object.assign({}, originalObject);
 
// 使用展开运算符
const shallowCopy = { ...originalObject };
 
// 手动遍历
function shallowCopy(original) {
  const copy = {};
  for (let key in original) {
    if (original.hasOwnProperty(key)) {
      copy[key] = original[key];
    }
  }
  return copy;
}

深拷贝

JavaScript 中实现深拷贝的方法有:

  1. 使用 JSON.parse(JSON.stringify())(注意,这种方法不能处理含有循环引用的对象或者不是纯 JavaScript 对象的值)
  2. 使用 lodashcloneDeep 方法
  3. 手动递归复制



// 使用 JSON.parse(JSON.stringify())
const deepCopy = JSON.parse(JSON.stringify(originalObject));
 
// 使用 lodash
const deepCopy = _.cloneDeep(originalObject);
 
// 手动递归
function deepCopy(original) {
  if (original === null || typeof original !== 'object') {
    return original;
  }
 
  const copy = Array.isArray(original) ? [] : {};
  for (let key in original) {
    if (original.hasOwnProperty(key)) {
      copy[key] = deepCopy(original[key]);
    }
  }
  return copy;
}

注意

  • 浅拷贝只复制最外层的属性,如果属性是对象或数组,则复制的是引用。
  • 深拷贝会递归复制所有层级的属性,对于对象中的每个对象都会创建一个新的实例。
  • 在实际应用中,根据对象的复杂性,可能需要更复杂的深拷贝实现,处理例如循环引用的情况。
2024-08-10

在Linux系统中配置Java环境变量,你需要找到你的Java安装路径,然后编辑你的shell配置文件(如.bashrc.bash_profile),添加相应的环境变量。以下是一个基本的示例:

  1. 打开终端。
  2. 输入以下命令找到Java的安装路径:

    
    
    
    sudo update-alternatives --config java

    或者,如果你是手动安装Java,你可以使用which java命令查找java可执行文件的位置。

  3. 找到你的Java安装目录后,编辑.bashrc.bash_profile文件:

    
    
    
    nano ~/.bashrc

    或者使用你喜欢的任何文本编辑器。

  4. 在文件的末尾添加以下环境变量(假设你的Java安装在/usr/lib/jvm/java-11-openjdk-amd64/):

    
    
    
    export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/
    export PATH=$JAVA_HOME/bin:$PATH

    注意替换/usr/lib/jvm/java-11-openjdk-amd64/为你实际的Java安装路径。

  5. 保存并关闭文件。
  6. 使环境变量更改生效:

    
    
    
    source ~/.bashrc
  7. 验证Java环境变量配置是否成功:

    
    
    
    echo $JAVA_HOME   # 应该输出你的Java安装目录
    java -version     # 应该输出安装的Java版本

以上步骤会设置JAVA_HOME环境变量,并将Java的可执行文件目录添加到PATH变量中,使你可以在任何目录下运行Java命令。

2024-08-10

在Linux中,您可以使用nohup命令配合&符号来在后台运行Java项目。nohup命令可以防止在您退出终端后程序被中断,而&则是将程序放入后台执行。

以下是一个示例命令,它将启动一个Java应用程序并将输出重定向到当前目录下的app.log文件中,即使您退出当前会话,Java应用程序也会继续运行:




nohup java -jar your-application.jar > app.log 2>&1 &

解释:

  • java -jar your-application.jar 是启动Java应用程序的命令。
  • > 是重定向标准输出到文件的符号。
  • app.log 是输出文件的名称。
  • 2>&1 是将标准错误(stderr,文件描述符为2)重定向到标准输出(stdout,文件描述符为1)。
  • & 是将命令放入后台执行。
  • nohup 是使命令忽略挂断信号。

执行上述命令后,您会得到一个进程的PID,可以使用kill命令通过这个PID来停止程序,或者使用jobs命令查看并管理后台任务。

2024-08-10

ZooKeeper是一个开源的分布式协调服务,它提供了一个简单的接口来实现分布式系统的同步服务。在Java后端中,ZooKeeper常被用作服务注册与发现、配置管理、集群管理等方面。

以下是一个使用ZooKeeper的简单示例,展示了如何在Java中创建一个ZooKeeper客户端,并在ZooKeeper中创建一个节点:




import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.CreateMode;
 
public class ZooKeeperExample {
    private static String connectString = "localhost:2181";
    private static int sessionTimeout = 2000;
    private ZooKeeper zkClient;
 
    public void connectToZookeeper() throws Exception {
        zkClient = new ZooKeeper(connectString, sessionTimeout, event -> {});
    }
 
    public void createZnode(String path, String data) throws KeeperException, InterruptedException {
        String result = zkClient.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("Created znode " + result);
    }
 
    public static void main(String[] args) {
        ZooKeeperExample example = new ZooKeeperExample();
        try {
            example.connectToZookeeper();
            example.createZnode("/myznode", "Hello, ZooKeeper");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,我们首先导入了必要的ZooKeeper类。然后定义了连接字符串connectString和会话超时时间sessionTimeout。在connectToZookeeper方法中,我们创建了一个ZooKeeper实例,并在main方法中调用它来连接到ZooKeeper服务器。createZnode方法用于创建一个新的节点,其中包含指定的数据。

这只是ZooKeeper功能的一个简单介绍,ZooKeeper还有更多复杂的使用场景和特性,如监听节点变化、控制访问权限等。

2024-08-10

以下是一个简单的Java网页爬虫示例,使用java.net.HttpURLConnection来获取网页内容。




import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
 
public class SimpleWebCrawler {
 
    public static void main(String[] args) {
        String urlToCrawl = "https://www.example.com";  // 替换为你想爬取的网址
        try {
            URL url = new URL(urlToCrawl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.connect();
 
            InputStreamReader inputStreamReader = new InputStreamReader(connection.getInputStream());
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
 
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
 
            bufferedReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这段代码创建了一个简单的网页爬虫,它连接到指定的URL,读取网页内容,并打印到控制台。这个例子没有处理更复杂的情况,比如多线程下载、页面解析、重试逻辑、cookie管理、处理重定向等,但它展示了基本的爬虫实现方法。

2024-08-10



import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
 
public class JDSpider {
    public static void main(String[] args) {
        String url = "https://www.jd.com/";
        try {
            // 设置请求头,模拟浏览器访问
            Document doc = Jsoup.connect(url)
                    .userAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36")
                    .get();
 
            // 解析HTML并获取需要的数据
            Elements elements = doc.select("div.gl-item");
            for (Element element : elements) {
                Elements imgElements = element.select("img.gl-i-img");
                String imageUrl = imgElements.attr("data-lazy-img"); // 使用'data-lazy-img'属性获取图片链接
                String price = element.select("div.p-price").text();
                String name = element.select("div.p-name em").text();
                System.out.println("图片链接:" + imageUrl);
                System.out.println("价格:" + price);
                System.out.println("商品名称:" + name);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这段代码使用了jsoup库来模拟浏览器请求京东首页,并解析出了图片、价格和商品名称信息。注意,由于爬取的数据涉及到个人隐私和网站版权,所以在实际应用中应确保遵守相关法律法规,并尊重网站的robot.txt规则以及版权政策。此外,为了避免被追踪,应该设置合适的User-Agent,并在爬取数据时采取适当的间隔和访问策略。

2024-08-10

泛型是Java中一个重要的部分,它允许在定义类或者方法时使用类型变量,这个类型变量可以在声明变量、创建对象、调用方法的时候才明确指定。

泛型的主要目的是为了创建可以按类型进行参数化的类或者方法,泛型的类或者方法可以以一种灵活的方式实现,而不需要进行类型转换。

下面是一个简单的泛型类的例子:




public class Box<T> {
    private T t;
 
    public Box(T t) {
        this.t = t;
    }
 
    public void set(T t) {
        this.t = t;
    }
 
    public T get() {
        return t;
    }
}

在这个例子中,T 是一个类型变量,它代表了一个未知的类型。当创建 Box 类的实例时,我们可以指定这个类型变量的具体类型:




Box<Integer> integerBox = new Box<>(10);
Box<String> stringBox = new Box<>("Hello");
 
System.out.println(integerBox.get()); // 输出 10
System.out.println(stringBox.get());  // 输出 Hello

泛型也可以用在方法上,例如:




public class Util {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }
}

在这个例子中,<T> 表示这是一个泛型方法,它可以接受任何类型的数组。

泛型还可以有多个类型变量,例如:




public class Pair<T, U> {
    private T first;
    private U second;
 
    public Pair(T first, U second) {
        this.first = first;
        this.second = second;
    }
 
    public T getFirst() {
        return first;
    }
 
    public U getSecond() {
        return second;
    }
}

在这个例子中,Pair 类接受两个不同的类型参数 TU

泛型的一个重要好处是类型检查,它可以在编译时而不是运行时检查类型安全,这可以帮助我们在编程时减少错误。

2024-08-10

Java 反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 的反射机制。

Java 反射机制的核心类:

  1. Class:反射的核心类,可通过此类的实例获取类的属性、方法等信息。
  2. Field:Java 类中的属性信息,可通过 Class 类的 getFieldsgetDeclaredFields 方法获取 Field 实例。
  3. Method:Java 类中的方法信息,可通过 Class 类的 getMethodsgetDeclaredMethods 方法获取 Method 实例。
  4. Constructor:Java 类中的构造方法信息,可通过 Class 类的 getConstructorsgetDeclaredConstructors 方法获取 Constructor 实例。

Java 反射机制的高级应用:

  1. 动态加载类:使用 Class.forName("类的全限定名") 动态加载类。
  2. 动态创建对象:使用 Class 实例的 newInstance 方法创建对象。
  3. 动态调用方法:使用 Method 实例的 invoke 方法调用对象的方法。
  4. 动态访问属性:使用 Field 实例的 getset 方法访问和修改对象的属性值。
  5. 动态代理:利用 Proxy 类和 InvocationHandler 接口实现动态代理。

示例代码:




// 动态加载类
Class<?> clazz = Class.forName("com.example.MyClass");
 
// 动态创建对象
Object myObject = clazz.newInstance();
 
// 动态获取方法
Method myMethod = clazz.getMethod("myMethodName", String.class);
 
// 动态调用方法
myMethod.invoke(myObject, "parameterValue");
 
// 动态访问属性
Field myField = clazz.getDeclaredField("myFieldName");
myField.setAccessible(true); // 设置私有属性可访问
myField.set(myObject, "fieldValue");
 
Object fieldValue = myField.get(myObject);

以上代码展示了如何使用 Java 反射机制进行基本的类操作,如加载、创建对象、调用方法和访问属性。在实际应用中,反射机制常用于框架开发,如 Spring 和 Hibernate,以及各种 ORM 框架中,用于实现类的动态加载和运行时元数据的操作。

2024-08-10

在Java中遍历Map的方式主要有四种:

  1. 使用for-each循环
  2. 使用Iterator
  3. 使用entrySet
  4. 使用Java 8 Stream API

下面是每种方式的示例代码:

  1. 使用for-each循环:



Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
 
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
  1. 使用Iterator:



Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
 
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, Integer> entry = iterator.next();
    System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
  1. 使用entrySet:



Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
 
for (String key : map.keySet()) {
    System.out.println("Key = " + key + ", Value = " + map.get(key));
}
  1. 使用Java 8 Stream API:



Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
 
map.forEach((key, value) -> System.out.println("Key = " + key + ", Value = " + value));

以上四种方式都可以用来遍历Java中的Map,但是在实际应用中,根据具体情况选择最合适的方式。例如,如果需要同时获取键和值,使用for-each循环和Java 8 Stream API是最直接的;如果需要在遍历过程中对Map做修改,应该使用Iterator。

2024-08-10

在MyBatis-Plus中,@TableField注解的typeHandler属性用于指定该字段的类型处理器。如果你需要自定义字段类型的处理逻辑,可以通过实现TypeHandler接口来创建自定义的类型处理器。

以下是一个简单的例子,演示如何为MySQL的JSON字段创建一个自定义的类型处理器,并在@TableField中使用它:




import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.json.JSONObject;
import java.sql.*;
 
// 自定义的JSON类型处理器
public class JsonTypeHandler implements TypeHandler<JSONObject> {
    @Override
    public void setParameter(PreparedStatement ps, int i, JSONObject parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter.toString());
    }
 
    @Override
    public JSONObject getResult(ResultSet rs, String columnName) throws SQLException {
        return new JSONObject(rs.getString(columnName));
    }
 
    @Override
    public JSONObject getResult(ResultSet rs, int columnIndex) throws SQLException {
        return new JSONObject(rs.getString(columnIndex));
    }
 
    @Override
    public JSONObject getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return new JSONObject(cs.getString(columnIndex));
    }
}
 
// 实体类使用@TableField注解
@TableName("your_table_name")
public class YourEntity {
    // 使用自定义的类型处理器
    @TableField(value = "json_column", typeHandler = JsonTypeHandler.class)
    private JSONObject jsonField;
 
    // 其他字段和getter/setter
}

在这个例子中,JsonTypeHandler实现了TypeHandler接口,用于处理JSON类型的字段。在YourEntity实体类中,@TableField注解的typeHandler属性被设置为JsonTypeHandler.class,这样MyBatis-Plus就会使用这个自定义的类型处理器来处理jsonField字段。这样,当你从数据库读取数据或将数据写入数据库时,MyBatis-Plus会使用JsonTypeHandler来正确地处理JSON字段。