《Java编程思想》读书笔记分享

Buck电路中自举电容串联小电阻的作用 在Buck电路中,自举电容(Cboot)驱动高边MOSFET时,因寄生电感和电容会形成LC谐振回路,导致SW节点高频振铃,引发EMI问题。串联小电阻(Rboot)可引入阻尼效应,抑制振铃、改善波形和EMI性能,但会略微增加开关损耗。设计时建议预留可调电阻位(2Ω~20Ω),根据实测结果平衡效率与系统稳定性,提升电源可靠性。这一措施是优化开关电源EMC的常见实践。 阅读详情

第十九章:枚举类型

关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。

enum的基本特性

我们已经知道,调用enum的values()方法,可以遍历enum实例。values()方法返回enum实例的数组,而且该数组中的元素严格保持其在enum中声明时的顺序,因此你可以在循环中使用values()返回的数组。

创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum。下面的例子演示了Enum提供的一些功能∶

package enumerated;

/**
 * @author Mr.Sun
 * @date 2022年09月02日 15:58
 *
 * 枚举的基本特性
 */
public class EnumClass {
    public static void main(String[] args) {
        for(Shrubbery s : Shrubbery.values()) {
            System.out.println(s + " ordinal: " + s.ordinal());
            System.out.print(s.compareTo(Shrubbery.CRAWLING) + " ");
            System.out.print(s.equals(Shrubbery.CRAWLING) + " ");
            System.out.println(s == Shrubbery.CRAWLING);
            System.out.println(s.getDeclaringClass());
            System.out.println(s.name());
            System.out.println("----------------------");
        }

        // 从字符串名称生成枚举值
        for(String s : "HANGING CRAWLING GROUND".split(" ")) {
            Shrubbery shrub = Enum.valueOf(Shrubbery.class, s);
            System.out.println(shrub);
        }
    }
}

enum Shrubbery {
    GROUND, CRAWLING, HANGING
}

运行结果如下图:

ordinal()方法返回一个int值,这是每个enum实例在声明时的次序,从0开始。可以使用==来比较enum实例,编译器会自动为你提供equals()和hashCode()方法。Enum类实现了Comparable 接口,所以它具有compareTo()方法。同时,它还实现了Serializable接口。

如果在enum实例上调用getDeclaringClass()方法,我们就能知道其所属的enum类。name()方法返回enum实例声明时的名字,这与使用toString()方法效果相同。valueOf()是在Enum中定义的static方法,它根据给定的名字返回相应的enum实例,如果不存在给定名字的实例,将会抛出异常。

values()的神秘之处

前面已经提到,编译器为你创建的enum类都继承自Enum类。然而,如果你研究一下Enum 类就会发现,它并没有values()方法。可我们明明已经用过该方法了,难道存在某种“隐藏的”方法吗?我们可以利用反射机制编写一个简单的程序,来查看其中的究竟∶

 

package enumerated;

import utils.OSExecute;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author Mr.Sun
 * @date 2022年09月02日 16:10
 *
 * 使用反射机制研究枚举类的values()
 */
enum Explore {
    HERE, THERE
}

public class Reflection {

    public static Set<String> analyze(Class<?> enumClass) {
        System.out.println("----- Analyzing " + enumClass + " -----");
        System.out.println("Interfaces:");
        for (Type t : enumClass.getGenericInterfaces()) {
            System.out.println(t);
        }
        System.out.println("Base: " + enumClass.getSuperclass());
        System.out.println("Methods: ");
        Set<String> methods = new TreeSet<String>();
        for (Method method : enumClass.getMethods()) {
            methods.add(method.getName());
        }
        System.out.println(methods);
        return methods;
    }

    public static void main(String[] args) {
        Set<String> exploreMethods = analyze(Explore.class);
        Set<String> enumMethods = analyze(Enum.class);
        System.out.println("Explore.containsAll(Enum)? " + exploreMethods.containsAll(enumMethods));
        System.out.print("Explore.removeAll(Enum): ");
        exploreMethods.removeAll(enumMethods);
        System.out.println(exploreMethods);
        // Decompile the code for the enum:
        OSExecute.command("javap G:/github/cnblogs/gitee/thinking-in-java/out/production/thinking-in-java/enumerated/Explore.class");
    }

} /* Output:
----- Analyzing class enumerated.Explore -----
Interfaces:
Base: class java.lang.Enum
Methods: 
[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait]
----- Analyzing class java.lang.Enum -----
Interfaces:
java.lang.Comparable<E>
interface java.io.Serializable
Base: class java.lang.Object
Methods: 
[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait]
Explore.containsAll(Enum)? true
Explore.removeAll(Enum): [values]
Compiled from "Reflection.java"
final class enumerated.Explore extends java.lang.Enum<enumerated.Explore> {
  public static final enumerated.Explore HERE;
  public static final enumerated.Explore THERE;
  public static enumerated.Explore[] values();
  public static enumerated.Explore valueOf(java.lang.String);
  static {};
}
*///:~

答案是,values()是由编译器添加的static方法。可以看出,在创建Explore的过程中,编译器还为其添加了valueOf()方法。这可能有点令人迷惑,Enum类不是已经有valueOf()方法了吗。不过Enum中的valueOf()方法需要两个参数,而这个新增的方法只需一个参数。由于这里使用的Set只存储方法的名字,而不考虑方法的签名,所以在调用Explore.removeAl(Enum)之后,就只剩下【values】了。

从最后的输出中可以看到,编译器将Explore标记为final类,所以无法继承自enum。其中还有一个static的初始化子句,稍后我们将学习如何重定义该句。

由于擦除效应(在第15章中介绍过),反编译无法得到Enum的完整信息,所以它展示的Explore的父类只是一个原始的Enum,而非事实上的Enum

。

由于values()方法是由编译器插入到enum定义中的static方法,所以,如果你将enum实例向上转型为Enum,那么values()方法就不可访问了。不过,在Class中有一个getEnumConstants()方法,所以即便Enum接口中没有values()方法,我们仍然可以通过Class对象取得所有enum实例∶

enum Search {
    HITHER, YON
}


public class UpcastEnum {
    public static void main(String[] args) {
        Search[] values = Search.values();
        for (Search val : values) {
            System.out.println(val.name());
        }
        System.out.println("--------------");
        Enum e = Search.HITHER;
        for (Enum en : e.getClass().getEnumConstants()) {
            System.out.println(en);
        }
    }
}

运行结果如下:

使用EnumSet代替标志

Set是一种集合,只能向其中添加不重复的对象。当然,enum也要求其成员都是唯一的,所以enum看起来也具有集合的行为。不过,由于不能从enum中删除或添加元素,所以它只能算是不太有用的集合。Java SE5引入EnumSet,是为了通过enum创建一种替代品,以替代传统的"基于int的“位标志”。这种标志可以用来表示某种“开/关”信息,不过,使用这种标志,我们最终操作的只是一些bit,而不是这些bit想要表达的概念,因此很容易写出令人难以理解的代码。

EnumSet的设计充分考虑到了速度因素,因为它必须与非常高效的bit标志相竞争(其操作与HashSet相比,非常地快)。就其内部而言,它(可能)就是将一个long值作为比特向量,所以EnumSet非常快速高效。使用EnumSet的优点是,它在说明一个二进制位是否存在时,具有更好的表达能力,并且无需担心性能。EnumSet中的元素必须来自一个enum。下面的enum表示在一座大楼中,警报传感器的安放位置∶

 

package enumerated;

public enum AlarmPoints {
    STAIR1, STAIR2,
    LOBBY,
    OFFICE1, OFFICE2, OFFICE3, OFFICE4,
    BATHROOM, UTILITY, KITCHEN
}

然后,我们使用EnumSet来跟踪报警器的状态:

package enumerated;

import java.util.EnumSet;

import static enumerated.AlarmPoints.*;

/**
 * @author Mr.Sun
 * @date 2022年09月02日 17:18
 */
public class EnumSetTest {
    public static void main(String[] args) {
        EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class); // Empty set
        points.add(BATHROOM);
        System.out.println(points);

        points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
        System.out.println(points);

        points = EnumSet.allOf(AlarmPoints.class);
        points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
        System.out.println(points);

        points.removeAll(EnumSet.range(OFFICE1, OFFICE4));
        System.out.println(points);

        points = EnumSet.complementOf(points);
        System.out.println(points);
    }
}

运行结果如下图:

使用EnumMap

EnumMap是一种特殊的Map,它要求其中的键(key)必须来自一个enum。由于enum本身的限制,所以EnumMap在内部可由数组实现。因此EnumMap的速度很快,我们可以放心地使用enum实例在EnumMap中进行查找操作。不过,我们只能将enum的实例作为键来调用put()方法,其他操作与使用一般的Map差不多。

下面的例子演示了命令设计模式的用法。一般来说,命令模式首先需要一个只有单一方法的接口,然后从该接口实现具有各自不同的行为的多个子类。接下来,程序员就可以构造命令对象,并在需要的时候使用它们了∶

package enumerated;

import java.util.EnumMap;
import java.util.Map;

import static enumerated.AlarmPoints.*;

/**
 * @author Mr.Sun
 * @date 2022年09月02日 17:25
 *
 * 使用EnumMap
 */
interface Command{ void action(); }

public class EnumMapTest {
    public static void main(String[] args) {
        EnumMap<AlarmPoints, Command> em = new EnumMap<>(AlarmPoints.class);
        em.put(KITCHEN, () -> System.out.println("Kitchen fire!"));
        em.put(BATHROOM, () -> System.out.println("Bathroom alert!"));
        for(Map.Entry<AlarmPoints,Command> e : em.entrySet()) {
            System.out.print(e.getKey() + ": ");
            e.getValue().action();
        }
        try {
            // If there's no value for a particular key:
            em.get(UTILITY).action();
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}/* Output:
BATHROOM: Bathroom alert!
KITCHEN: Kitchen fire!
java.lang.NullPointerException
*///:~

 

与EnumSet一样,enum实例定义时的次序决定了其在EnumMap中的顺序。

main()方法的 最后部分说明,enum的每个实例作为一个键,总是存在的,但是如果你没有为这个键调用put()方法来存入相应的值的话,对应的值就是null。

常量相关的方法

Java的Enum有一个非常有趣的特性,即它允许程序员为enum实例编写方法,从而为每个enum实例赋予各自不同的行为,要实现常量相关的方法,你需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法。参考下面的例子:

package enumerated;

import java.text.DateFormat;
import java.util.Date;

public enum ConstantSpecificMethod {

    DATE_TIME {
        String getInfo() {
            return DateFormat.getDateInstance().format(new Date());
        }
    },
    CLASSPATH {
        String getInfo() {
            return System.getenv("CLASSPATH");
        }
    },
    VERSION {
        String getInfo() {
            return System.getProperty("java.version");
        }
    };

    abstract String getInfo();

    public static void main(String[] args) {
        for(ConstantSpecificMethod csm : values()) {
            System.out.println(csm.getInfo());
        }
    }
}/* Output:
2022-9-2
null
1.8.0_211
*///:~

使用enum的职责链

在职责链(Chain of Responsibility)设计模式中,程序员以多种不同的方式来解决一个问题,然后将它们链接在一起。当一个请求到来时,它遍历这个链,直到链中的某个解决方案能够处理该请求。

通过常量相关的方法,我们可以很容易地实现一个简单的职责链。我们以一个邮局的模型为例。邮局需要以尽可能通用的方式来处理每一封邮件,并且要不断尝试处理邮件,直到该邮件最终被确定为一封死信。其中的每一次尝试可以看作为一个策略(也是一个设计模式),而完整的处理方式列表就是一个职责链。

我们先来描述一下邮件。邮件的每个关键特征都可以用enum来表示。程序将随机地生成Mail对象,如果要减小一封邮件的GeneralDelivery为YES的概率,那最简单的方法就是多创建几个不是YES的enum实例,所以enum的定义看起来有点古怪。

我们看到Mail中有一个randomMail()方法,它负责随机地创建用于测试的邮件。而generator()方法生成一个Iterable对象,该对象在你调用next()方法时,在其内部使用randomMail()来创建Mail对象。这样的结构使程序员可以通过调用Mail.generator()方法,很容易地构造出一个foreach循环∶

package enumerated;

import utils.Enums;

import java.util.Iterator;

/**
 * @author Mr.Sun
 * @date 2022年09月02日 17:45
 *
 * 以邮局的模型为例,通过常量相关的方法,实现一个简单的职责链
 */
public class PostOffice {

    enum MailHandler {
        GENERAL_DELIVERY {
            boolean handle(Mail m) {
                switch (m.generalDelivery) {
                    case YES:
                        System.out.println("Using general delivery for " + m);
                        return true;
                    default:
                        return false;
                }
            }
        },

        MACHINE_SCAN {
            boolean handle(Mail m) {
                switch (m.scannability) {
                    case UNSCANNABLE:
                        return false;
                    default:
                        switch (m.address) {
                            case INCORRECT:
                                return false;
                            default:
                                System.out.println("Delivering " + m + " automatically");
                                return true;
                        }
                }
            }
        },
        VISUAL_INSPECTION {
            boolean handle(Mail m) {
                switch (m.readability) {
                    case ILLEGIBLE:
                        return false;
                    default:
                        switch (m.address) {
                            case INCORRECT:
                                return false;
                            default:
                                System.out.println("Delivering " + m + " normally");
                                return true;
                        }
                }
            }
        },
        RETURN_TO_SENDER {
            boolean handle(Mail m) {
                switch (m.returnAddress) {
                    case MISSING:
                        return false;
                    default:
                        System.out.println("Returning " + m + " to sender");
                        return true;
                }
            }
        };

        abstract boolean handle(Mail m);
    }

    static void handle(Mail m) {
        for(MailHandler handler : MailHandler.values()) {
            if(handler.handle(m)) {
                return;
            }
        }
        System.out.println(m + " is a dead letter");
    }

    public static void main(String[] args) {
        for(Mail mail : Mail.generator(10)) {
            System.out.println(mail.details());
            handle(mail);
            System.out.println("*****");
        }
    }
}

class Mail {
    // “否”会降低随机选择的概率:
    enum GeneralDelivery {YES, NO1, NO2, NO3, NO4, NO5}
    enum Scannability {UNSCANNABLE, YES1, YES2, YES3, YES4}
    enum Readability {ILLEGIBLE, YES1, YES2, YES3, YES4}
    enum Address {INCORRECT, OK1, OK2, OK3, OK4, OK5, OK6}
    enum ReturnAddress {MISSING, OK1, OK2, OK3, OK4, OK5}

    GeneralDelivery generalDelivery;
    Scannability scannability;
    Readability readability;
    Address address;
    ReturnAddress returnAddress;
    static long counter = 0;
    long id = counter++;

    public String toString() { return "Mail " + id; }

    public String details() {
        return toString() +
                ", General Delivery: " + generalDelivery +
                ", Address Scanability: " + scannability +
                ", Address Readability: " + readability +
                ", Address Address: " + address +
                ", Return address: " + returnAddress;
    }

    /**
     * 生成测试邮件
     */
    public static Mail randomMail() {
        Mail m = new Mail();
        m.generalDelivery= Enums.random(GeneralDelivery.class);
        m.scannability = Enums.random(Scannability.class);
        m.readability = Enums.random(Readability.class);
        m.address = Enums.random(Address.class);
        m.returnAddress = Enums.random(ReturnAddress.class);
        return m;
    }

    public static Iterable<Mail> generator(final int count) {
        return new Iterable<Mail>() {
            int n = count;
            public Iterator<Mail> iterator() {
                return new Iterator<Mail>() {
                    public boolean hasNext() { return n-- > 0; }
                    public Mail next() { return randomMail(); }
                    public void remove() { // Not implemented
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }
}
/* Output:
Mail 0, General Delivery: NO2, Address Scanability: UNSCANNABLE, Address Readability: YES3, Address Address: OK1, Return address: OK1
Delivering Mail 0 normally
*****
Mail 1, General Delivery: NO5, Address Scanability: YES3, Address Readability: ILLEGIBLE, Address Address: OK5, Return address: OK1
Delivering Mail 1 automatically
*****
Mail 2, General Delivery: YES, Address Scanability: YES3, Address Readability: YES1, Address Address: OK1, Return address: OK5
Using general delivery for Mail 2
*****
Mail 3, General Delivery: NO4, Address Scanability: YES3, Address Readability: YES1, Address Address: INCORRECT, Return address: OK4
Returning Mail 3 to sender
*****
Mail 4, General Delivery: NO4, Address Scanability: UNSCANNABLE, Address Readability: YES1, Address Address: INCORRECT, Return address: OK2
Returning Mail 4 to sender
*****
Mail 5, General Delivery: NO3, Address Scanability: YES1, Address Readability: ILLEGIBLE, Address Address: OK4, Return address: OK2
Delivering Mail 5 automatically
*****
Mail 6, General Delivery: YES, Address Scanability: YES4, Address Readability: ILLEGIBLE, Address Address: OK4, Return address: OK4
Using general delivery for Mail 6
*****
Mail 7, General Delivery: YES, Address Scanability: YES3, Address Readability: YES4, Address Address: OK2, Return address: MISSING
Using general delivery for Mail 7
*****
Mail 8, General Delivery: NO3, Address Scanability: YES1, Address Readability: YES3, Address Address: INCORRECT, Return address: MISSING
Mail 8 is a dead letter
*****
Mail 9, General Delivery: NO1, Address Scanability: UNSCANNABLE, Address Readability: YES2, Address Address: OK1, Return address: OK4
Delivering Mail 9 normally
*****
*///:~

职责链由enum MailHandler实现,而enum定义的次序决定了各个解决策略在应用时的次序。对每一封邮件,都要按此顺序尝试每个解决策略,直到其中一个能够成功地处理该邮件,如果所有的策略都失败了,那么该邮件将被判定为一封死信。

第二十章:注解

注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用某些数据。

定义注解

package annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}

除了@符号以外,@Test的定义很像一个空的接口。定义注解时,会需要一些元注解(meta-annotation),如@Target和@Retention。@Target用来定义你的注解将应用于什么地方(例如是一个方法或者一个域)。@Rectetion用来定义该注解在哪一个级别可用,在源代码中(SOURCE)、类文件中(CLASS)或者运行时(RUNTIME)。

没有元素的注解称为标记注解,例如上例种的@Test。

注解元素

注解元素可用的类型如下:

  • 所有基本类型(int, float, boolean等)
  • String
  • Class
  • enum
  • Annotation
  • 以上类型的数组

如果你使用了其它类型,编译器就会报错。注意,也不允许使用任何包装类型,不过由于自动打包的存在,这算不上什么限制。注解也可以作为元素的类型,也就是说,注解可以嵌套。

元注解

Java目前只内置了四种元注解,元注解专职负责注解其它的注解:

大多数时候,程序员主要是定义自己的注解,并编写自己的处理器来处理它们。

下面是一个简单的注解,我们可以用它来跟踪一个项目中的用例。如果一个方法或一组方法实现了某个用例的需求,那么程序员可以为此方法加上该注解。于是,项目经理通过计算已经实现的用例,就可以很好地掌控项目的进展。而如果要更新或修改系统的业务逻辑,则维护该项目的开发人员也可以很容易地在代码中找到对应的用例。

package annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    public int id();
    public String description() default "no description";
}

 

注意,id和description类似方法定义。由于编译器会对id进行类型检查,因此将用例文档的追踪数据库与源代码相关联是可靠的。description元素有一个default值,如果在注解某个方法时没有给出description的值,则该注解的处理器就会使用此元素的默认值。

在下面的类中,有三个方法被注解为用例∶

package annotations;

import java.util.List;

/**
 * @author Mr.Sun
 * @date 2022年09月03日 9:05
 *
 * 注解用例
 */
public class PasswordUtils {

    @UseCase(id = 47, description = "密码必须至少包含一个数字")
    public boolean validatePassword(String password) {
        return (password.matches("\\w*\\d\\w*"));
    }

    @UseCase(id = 48)
    public String encryptPassword(String password) {
        return new StringBuilder(password).reverse().toString();
    }

    @UseCase(id = 49, description = "新密码不能等于以前使用的密码")
    public boolean checkForNewPassword(List<String> prevPasswords, String password) {
        return !prevPasswords.contains(password);
    }
}

注解的元素在使用时表现为名一值对的形式,并需要置于@UseCase声明之后的括号内。在encryptPassword()方法的注解中,并没有给出description元素的值,因此,在UseCase的注解处理器分析处理这个类时会使用该元素的默认值。

你应该能够想象得到如何使用这套工具来“勾勒”出将要建造的系统,然后在建造的过程中逐渐实现系统的各项功能。

编写注解处理器

如果没有用来读取注解的工具,那注解也不会比注释更有用。使用注解的过程中,很重要的一个部分就是创建与使用注解处理器。Java SE5扩展了反射机制的API,以帮助程序员构造这类工具。同时,它还提供了一个外部工具apt帮助程序员解析带有注解的Java源代码。

下面是一个非常简单的注解处理器,我们将用它来读取PasswordUtils类,并使用反射机制查找@UseCase标记。我们为其提供了一组id值,然后它会列出在PasswordUtils种找到的用例,以及缺失的用例。

package annotations;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Mr.Sun
 * @date 2022年09月03日 9:11
 *
 * 编写注解处理器
 */
public class UseCaseTracker {

    public static void trackUseCases(List<Integer> useCases, Class<?> clazz) {
        for (Method m : clazz.getDeclaredMethods()) {
            UseCase useCase = m.getAnnotation(UseCase.class);
            if (useCase != null) {
                System.out.println("找到@UseCase:" + useCase.id() + " "  + useCase.description());
                useCases.remove(Integer.valueOf(useCase.id()));
            }
        }
        for (Integer i : useCases) {
            System.out.println("警告: 缺失用例:" + i);
        }
    }

    public static void main(String[] args) {
        List<Integer> useCases = new ArrayList<>();
        Collections.addAll(useCases, 47, 48, 49, 50);
        trackUseCases(useCases, PasswordUtils.class);
    }
} /* Output:
找到@UseCase:49 新密码不能等于以前使用的密码
找到@UseCase:47 密码必须至少包含一个数字
找到@UseCase:48 no description
警告: 缺失用例:50
*///:~

这个程序用到了两个反射的方法∶getDeclaredMethods()和getAnnotation(),它们都属于AnnotatedElement接口(Class、Method与Feld等类都实现了该接口)。getAnnoation()方法返回指定类型的注解对象,在这里就是UseCase。如果被注解的方法上没有该类型的注解,则返回null值。然后我们通过调用id()和description()方法从返回的UseCase对象中提取元素的值。其中,encriptPassword()方法在注解的时候没有指定description的值,因此处理器在处理它对应的注解时,通过description()方法取得的是默认值no description。

总结

枚举和注解其实在日常开发中都很熟悉,因为是非常基础的知识,本文也只是把模糊的概念和比较冷门的知识点记录下来,方便日后查阅,原来是打算把第二十一章的内容也放在本文的,但发现并发这章内容太多了,限于篇幅,还是打算单独写一篇文章进行记录,而且并发也是比较重要的基础知识。

微信小程序 - 实现身份证OCR识别人像面与国徽面并提取字段信息,详解从手机相册选取或打开摄像头拍照上传身份证正反面图片ocr文字识别数据填充表单(提供免费纯前端ocr识别插件+百度OCR识别API) 微信小程序身份证识别,微信小程序身份证拍照、上传并OCR识别,微信小程序实现OCR扫描识别,微信小程序识别身份证免费插件,微信小程序实现身份证识别功能。ocr识别插件。微信身份证拍照后进行ocr识别,微信小程序OCR扫描识别身份证,OCR身份证实名认证+图片上传+拍照上传图片,微信小程序之身份证照片正反上传,小程序OCR身份证识别,微信小程序实现文字识别-ocr插件,微信小程序利用百度AI实现扫描身份证获取信息功能,基于微信小程序OCR插件实现身份证拍照、上传并OCR识别的示例demo,将上传的照片传到服务 阅读详情

相关推荐

葵花 Himawari 8/9号数据学习记录-01-数据初识

葵花卫星学习记录,第一章:卫星的来源与卫星数据的波段介绍

weixin_44111909的博客 539

《Java编程思想》读书笔记(四)

三年之前就买了《Java编程思想》这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第十七章到第十八章的内容,这一次记录的是第十九章到第二十章的内容,相关示例代码放在码云上了,码云地址:https://gitee.com/reminis_com/thinking-in-java

程序员波特 475

STM32 Nucleo-144开发板开箱bring-up

最近做项目,用到STM32F446ZET6这款MCU,为了赶进度,前期软件需要提前开发,于是在某宝买了对应主控的官方开发板,为了避免遗忘,后面能翻出来回忆一些细节,就形成了本篇的主要内容,同时也希望能够帮助刚上手的小伙伴少走弯路,如果对你有帮助请点赞、收藏、转发,谢谢!

汽车电子软件领域知识分享 2680

《Java 编程思想》读书笔记

第一章 对象导论 1.抽象过程 所有编程语言都提供抽象机制 解决问题的复杂性直接取决于抽象的类型和质量 抽象的类型指 “所抽象的是什么?” 汇编语言是对底层机器的轻微抽象 “命令式” 语言(如 C、Java 等等)是对汇编语言的抽象 OOP 是根据问题描述问题,而不是根据运行解决方案的计算机描述问题 面向对象的特点 万物皆对象,如 狗、房子、服务等等都可以作为对象 程序是对象的集合 对象里面可...

南京的博客 1003

深入解析SAP F4帮助函数F4IF_INT_TABLE_VALUE_REQUEST的回调机制

本文深入解析了SAP ABAP开发中F4IF_INT_TABLE_VALUE_REQUEST函数的回调机制。通过剖析其工作原理与核心参数,详细阐述了如何利用回调函数实现F4帮助的多值返回、动态数据过滤及选择后校验等高级定制功能,并提供了实用的代码示例与调试技巧,帮助开发者高效应对复杂的屏幕交互需求。

weixin_29213827的博客 198

Rockchip rv1126平台在gstreamer1中添加OpenCv实现视频流负片的效果

rv1126 gstreamer opencv negetive effect

Aisy_D的专栏 1463

Java编程思想学习笔记(详细)

访问权限 可以对成员变量、方法有以下四种权限设置: private,你无法访问:本类中可见,本包的类、其他包的类都不可见 protected,继承访问权限:本包的类可见,其他包的类不可见,其他包的类继承后可见 public,接口访问权限:本类、本包的类、其他包的类都可见 默认包访问权限:本包的类可见,其他包的类不可见,其他包的类继承后也不可见 每个文件只能由一个public类,提供单一的...

Bingmous的博客 1009

告别交越失真:手把手教你设计高性能互补输出级电路(LTspice实测)

本文深入剖析音频放大器中常见的交越失真问题,并提供基于LTspice仿真的实战解决方案。通过对比二极管偏置、Vbe倍增器和集成运放驱动三种经典方案,手把手指导设计高性能互补输出级电路,涵盖原理分析、仿真验证及实物调试要点,助力消除音频失真,提升音质。

weixin_29190573的博客 553

Java 编程思想 读书笔记

Java编程思想_第一章_对象导论 三、每个对象都提供服务 3.1 将对象看作是服务提供者的好处:有利于提高对象的内聚性 这里的举例是一个打印模块在良好的面向对象程序设计中,每个对象都可以很好的完成一项任务,但他不试图做更多的事。待办待办......

nmsldkbb的博客 812

【测绘程序设计】——潮汐调和分析

最小二乘潮汐调和分析源码、使用示例及数据

C_xxy's blog 2653

大模型算法岗常见面试题100道

LangChain是一个用于构建和运行大型语言模型应用的开源框架。它提供了一套工具和组件,帮助开发者将大型语言模型(如 GPT-3)与其他工具和API结合,以完成更复杂的任务。1、LangChain包含哪些核心概念?Components:可重用的模块,例如API调用、数据库查询等。Chains:将多个Components链接在一起以完成特定任务的流程。Prompt Templates: 用于指导语言模型生成输出的文本模板。Output Parsers:解析语言模型输出的工具。

具身机器人和通用AI 1962

《Java编程思想》读书笔记(五)

本文是《Java编程思想》读书笔记系列的最后一章,本章的内容很多,需要细读慢慢去理解,文中的示例最好在自己电脑上多运行几次,相关示例完整代码放在码云上了,码云地址:https://gitee.com/reminis_com/thinking-in-java

程序员波特 799

数仓建模—维度建模之维度表

首先要学会识别维度,维度是相对稳定的、可抽象具有共性的、固有的(天然的)描述度量或事实的上下文属性信息,失去维度信息的度量信息是没有意义的或者说是失去维度信息的事实表是没有意义的。其次要掌握维度表的设计原则和设计流程,从而更加科学的设计维度表,其实这些东西都是术,我们一直都说一句话大道至简,其实如果你掌握了精髓,抛开这些术也可以做到得心应手,这就像张无忌练太极剑一样,忘记了才能达到道的境界,也就是无我,如果做不到就记住这些原则加以应用从而达到术的境界。

2401_84052244的博客 4364

思维导图学《Java编程思想》

http://blog.youkuaiyun.com/u010154380/article/details/72701303 用了1个月时间,把《Java编程思想》读了一遍。然后用Mindjet Mindmanager做了读书笔记,跟大家分享一下~ 这个思维导图的读书笔记没有涵盖书中的所有知识点,但是作为框架和读书的思路比较清晰。 思维导图中有很多代码在note中,没有贴到博

wongdei123321的博客 660

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

读书笔记一

    2018-03-10,第一篇读书笔记以及之后的大致规划。    目前手里有四本书《java编程思想》、《算法导论》、《C++Primer Plus》以及关于python数据分析的书。    在今年7月的时候,看完以上书籍,并且建立一套采集数据,分析数据,展示数据的系统。看书期间,完成系统整体到个个模块的设计。今天以《java编程思想》学习为主,目标是书的前两章。第一章对象导论,五个特性:1...

u011933382的博客 603

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

如何用思维导图学 Java 编程?(收藏版)

点击上方“开发者技术前线”,选择“星标”13:21 在看真爱作者:被称为L的男人,地址:http://www.jianshu.com/p/659f2599bde9如何...

开发者技术前线-DevolperFront 479

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Java学习笔记-序

最近开始学习java了,上班看书看得经常瞌睡,有时候想起来觉得挺重要的知识点想记在哪里又害怕忘记了,于是乎突然想到了博客园,所以今天上午就决定记在院子里了,先写了8是因为已经看到第八章了(读的是Java编程思想第四版完整中文,有想要的朋友也可以给我发信息问我要啊),以后会慢慢把之前的章节都补起来,打算把自己看到觉得重要的内容做一个读书笔记,这样方便自己以后看,也可以分享给大家~没了~ ...

weixin_30580341的博客 82

快速学习编程技术的链接

菜鸟教程 http://www.runoob.com/ w3cschool https://www.w3cschool.cn/ Java编程思想读书笔记 https://blog.youkuaiyun.com/leonliu06/article/details/78638841 Effective Java通俗理解 https://www.cnblogs.com/yulinfeng/archive/2017/...

cjxunooo的博客 448

《Java编程思想》读书笔记

第一章 对象导论 1.1 抽象过程 Smalltalk的五个基本特性 万物皆为对象; 程序是对象的集合,它们通过发送消息来告知彼此所要做的;可以把消息想象为对某个特定对象的方法的调用请求。 每个对象都有自己的由其他对象所构成的存储; 每个对象都拥有其类型; 某一特定类型的所有对象都可以接收同样的消息。 Booch对于对象的描述 对象具有状态、行为和标识。这意味着每个对象都可以拥有内部数据...

qiaominghe的博客 369

Java编程思想笔记目录

祝各位 程序???? 节日快乐???? ![在这里插入图片描述](https://img-blog.csdnimg.cn/2020102311591445.gif#pic_center) 写在前面:   前阵子,小名“偷偷地”发了一批文章,是小名的一些读书笔记✍,那么为什么要把笔记发出来呢,嘻嘻,当然是小名的????不太灵光啦????但是吧,小名坐地铁或者走在大街上的时候,脑袋里总是会想一些有的没的,不排除会想一些Java基础的知识,时常联系不起来,就有可能别扭一整天。所以呢,把它发出来一方面是为了和大

小名同学 23万+

Java编程思想 一切都是对象(读书笔记)

一. 用引用操纵对象 1.String s 只是一个引用, 初始化时 (“aaa”)才与对象"aaa"关联. 2. 不是一切皆类,有如下几种基本类型(存储于栈区): 基本类型 大小 最小值 最大值 包装器类型 boolean - - - Boolean char 16-bit Unicode o Unicode 2^16 - 1 C

qq_35721336的博客 328

关于阅读java编程思想和effective java的一些看法

个人认为,java编程思想并不适合当作新手入门书籍来看,它更多是像给已经使用过java的人群对于基础的一些查缺补漏,有点像一本大部头的工具书,目前该书已看至第十章 -- 内部类, 而effective java这本书,更多是如所说的,是一本分享经验与指引你避免走弯路的经典著作,针对如何编写高效、设计优良的程序提出了最实用、最权威的指导方针,目前该书只看至第三章。我本来是想看完一个章节来写一...

weixin_34257076的博客 1013

Java基于Springboot+Vue的读书笔记分享交流系统 cf856

随着时代的发展,越来越多的图书和知识需要人们进行汲取,为了让大家有一个更好的读书笔记分享和交流的平台我们通过Java语言和Springboot开发了本系统,本系统在前端使用Vue渐进式JS框架进行开发,实现了前后端分类的设计。本文从管理员、用户的功能要求出发,读书笔记共享平台系统中的功能模块主要是实现管理员:首页、个人中心、用户管理、笔记分享管理、个人笔记管理、管理员管理、交流互动、系统管理。

QQ860234001的专栏 656

用Spring Boot搭建的读书笔记分享平台

系统所要实现的功能分析,对于现在网络方便的管理,据数据调查显示,相比过去增长较快,用户通过网上登录的方式已经形成了一种依赖,不管需要什么信息内容,直接上网查找,参考比较大,对读书笔记共享平台的类型和特点的内容信息有了详细的了解,让用户更有针对性的选择。读书笔记共享平台软件是一款方便、快捷、实用的信息服务查询软件。笔记分享管理:管理员在通过笔记分享管理进入页面可以查看笔记名称、笔记类型、笔记标签、笔记图片、分享日期、用户名、邮箱、审核回复、审核状态等信息,并进行详情、删除、修改操作,如图5-4所示。

2401_85761003的博客 855

《Java编程思想》读书笔记1--感受

    很厚的一本书,在大一末的时候导师推荐了这本书,在学校怀着完成学业的目的读完它。2016年,工作之后,发现真正写代码的时候,很多都是一知半解。比如,容器和泛型应该怎么用,并发该怎么写,异常该如何处理,IO的框架是怎么回事。不过,总算知道这些东西的存在。又花了半年的时间,第二遍。这次,带着问题有目的性地把相应的章节仔细地重读了一遍。工作中的问题得到顺利解决了,但是好奇心没有得到充分的满足,很多...

minipanda-luotj的博客 742

基于java的SpringBoot框架读书笔记共享平台

本论文主要论述了如何使用JAVA语言开发一个读书笔记共享平台 ,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论述读书笔记共享平台的当前背景以及系统开发的目的,后续章节将严格按照软件开发流程,对系统进行各个阶段分析设计。本论文介绍了读书笔记共享平台系统中分析到设计最后到开发的全过程,在开发过程中,通过在这些读书笔记共享平台系统,利用专业网站已有的系统进行活动本系统有管理员、用户及前台首页。

这就是程序猿的博客 989
上一篇: mysql查询 limit 1000,10 和limit 10 速度一样快吗?如果我要分页,我该怎么办?
下一篇: Spring源码分析之依赖注入(二)
java_lujj
博客等级 码龄4年 18粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值