• [技术干货] mysql怎样查询被锁的表
    1.查看表是否被锁:(1)直接在mysql命令行执行:show engine innodb status\G。(2)查看造成死锁的sql语句,分析索引情况,然后优化sql。(3)然后show processlist,查看造成死锁占用时间长的sql语句。(4)show status like ‘%lock%。2.查看表被锁状态和结束死锁步骤:(1)查看表被锁状态:show OPEN TABLES where In_use > 0; 这个语句记录当前锁表状态 。(2)查询进程:show processlist查询表被锁进程;查询到相应进程killid。(3)分析锁表的SQL:分析相应SQL,给表加索引,常用字段加索引,表关联字段加索引。(4)查看正在锁的事物:SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS。(5)查看等待锁的事物:SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS。扩展资料MySQL锁定状态查看命令:Checking table:正在检查数据表(这是自动的)。Closing tables:正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的操作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。Connect Out:复制从服务器正在连接主服务器。Copying to tmp table on disk:由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存。Creating tmp table:正在创建临时表以存放部分查询结果。deleting from main table:服务器正在执行多表删除中的第一部分,刚删除第一个表。deleting from reference tables:服务器正在执行多表删除中的第二部分,正在删除其他表的记录。Flushing tables:正在执行FLUSH TABLES,等待其他线程关闭数据表。Killed:发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。Locked:被其他查询锁住了。Sending data:正在处理SELECT查询的记录,同时正在把结果发送给客户端。Sorting for group:正在为GROUP BY做排序。Sorting for order:正在为ORDER BY做排序。Opening tables:这个过程应该会很快,除非受到其他因素的干扰。例如,在执ALTER TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。Removing duplicates:正在执行一个SELECT DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。Reopen table:获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。Repair by sorting:修复指令正在排序以创建索引。Repair with keycache:修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。Searching rows for update:正在讲符合条件的记录找出来以备更新。它必须在UPDATE要修改相关的记录之前就完成了。Sleeping:正在等待客户端发送新请求。System lock:正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加--skip-external-locking参数来禁止外部系统锁。Upgrading lock:INSERT DELAYED正在尝试取得一个锁表以插入新记录。Updating:正在搜索匹配的记录,并且修改它们。User Lock:正在等待GET_LOCK()。Waiting for tables:该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个表。waiting for handler insert:INSERT DELAYED已经处理完了所有待处理的插入操作,正在等待新的请求。原文链接:https://m.php.cn/article/487451.html
  • [技术干货] Spring boot 注解@Async不生效 无效 不起作用
     今天在做公司项目时,有一个发邮件的需求。所以写了一个发送邮件的方法后来发现发邮件很慢,导致接口响应也很慢。于是我便想到要使用异步调用去处理这个方法。于是我把注解@Async 加到了自己service类下的一个发邮件的一个方法,后来发现并没有生效。   调用处代码:  我在以下两个方法上都试过加上@Async注解 可是都并没有生效 发送邮件代码: 我思考了一下可能应该是因为aop代理的缘故,被调用方法 和 调用处的代码都处在同一个类,所以只是相当于本类调用,并没有使用代理类 从而@Async并没有产生效果。 于是我查询了一下资料,证实了我的想法,于是我把@Async 注解放到了工具类 MailService 下   修改了注解的添加位置后,接口调用明显速度加快了,成功! 总结: 失效原因 1.@SpringBootApplication启动类当中没有添加@EnableAsync注解。 2.异步方法使用注解@Async的返回值只能为void或者Future。 3.没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器管理。   解决方法: 这里具体说一下第三种情况的解决方法。 1.注解的方法必须是public方法。 2.方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的。 3.如果需要从类的内部调用,需要先获取其代理类。   扩展阅读: https://blog.csdn.net/YoungLee16/article/details/88398045 https://blog.csdn.net/QYHuiiQ/article/details/85014983 ———————————————— 原文链接:https://blog.csdn.net/weixin_37760377/article/details/103627676 
  • [技术干货] Spring Boot中使用@Async实现异步调用
    什么是“异步调用”?“异步调用”对应的是“同步调用”,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;异步调用指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序。同步调用下面通过一个简单示例来直观的理解什么是同步调用:定义Task类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内)@Component public class Task { public static Random random =new Random(); public void doTaskOne() throws Exception { System.out.println("开始做任务一"); long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(10000)); long end = System.currentTimeMillis(); System.out.println("完成任务一,耗时:" + (end - start) + "毫秒"); } public void doTaskTwo() throws Exception { System.out.println("开始做任务二"); long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(10000)); long end = System.currentTimeMillis(); System.out.println("完成任务二,耗时:" + (end - start) + "毫秒"); } public void doTaskThree() throws Exception { System.out.println("开始做任务三"); long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(10000)); long end = System.currentTimeMillis(); System.out.println("完成任务三,耗时:" + (end - start) + "毫秒"); } }在单元测试用例中,注入Task对象,并在测试用例中执行doTaskOne、doTaskTwo、doTaskThree三个函数。@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) public class ApplicationTests { @Autowired private Task task; @Test public void test() throws Exception { task.doTaskOne(); task.doTaskTwo(); task.doTaskThree(); } }执行单元测试,可以看到类似如下输出:开始做任务一 完成任务一,耗时:4256毫秒 开始做任务二 完成任务二,耗时:4957毫秒 开始做任务三 完成任务三,耗时:7173毫秒任务一、任务二、任务三顺序的执行完了,换言之doTaskOne、doTaskTwo、doTaskThree三个函数顺序的执行完成。异步调用上述的同步调用虽然顺利的执行完了三个任务,但是可以看到执行时间比较长,若这三个任务本身之间不存在依赖关系,可以并发执行的话,同步调用在执行效率方面就比较差,可以考虑通过异步调用的方式来并发执行。在Spring Boot中,我们只需要通过使用@Async注解就能简单的将原来的同步函数变为异步函数,Task类改在为如下模式:@Component public class Task { @Async public void doTaskOne() throws Exception { // 同上内容,省略 } @Async public void doTaskTwo() throws Exception { // 同上内容,省略 } @Async public void doTaskThree() throws Exception { // 同上内容,省略 } }为了让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsync,如下所示:@SpringBootApplication @EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }此时可以反复执行单元测试,您可能会遇到各种不同的结果,比如:没有任何任务相关的输出有部分任务相关的输出乱序的任务相关的输出原因是目前doTaskOne、doTaskTwo、doTaskThree三个函数的时候已经是异步执行了。主程序在异步调用之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束了,导致了不完整或是没有输出任务相关内容的情况。注: @Async所修饰的函数不要定义为static类型,这样异步调用不会生效异步回调为了让doTaskOne、doTaskTwo、doTaskThree能正常结束,假设我们需要统计一下三个任务并发执行共耗时多少,这就需要等到上述三个函数都完成调动之后记录时间,并计算结果。那么我们如何判断上述三个异步调用是否已经执行完成呢?我们需要使用Future<T>来返回异步调用的结果,就像如下方式改造doTaskOne函数:@Async public Future<String> doTaskOne() throws Exception { System.out.println("开始做任务一"); long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(10000)); long end = System.currentTimeMillis(); System.out.println("完成任务一,耗时:" + (end - start) + "毫秒"); return new AsyncResult<>("任务一完成"); }按照如上方式改造一下其他两个异步函数之后,下面我们改造一下测试用例,让测试在等待完成三个异步调用之后来做一些其他事情。@Test public void test() throws Exception { long start = System.currentTimeMillis(); Future<String> task1 = task.doTaskOne(); Future<String> task2 = task.doTaskTwo(); Future<String> task3 = task.doTaskThree(); while(true) { if(task1.isDone() && task2.isDone() && task3.isDone()) { // 三个任务都调用完成,退出循环等待 break; } Thread.sleep(1000); } long end = System.currentTimeMillis(); System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒"); }看看我们做了哪些改变:在测试用例一开始记录开始时间在调用三个异步函数的时候,返回Future<String>类型的结果对象在调用完三个异步函数之后,开启一个循环,根据返回的Future<String>对象来判断三个异步函数是否都结束了。若都结束,就结束循环;若没有都结束,就等1秒后再判断。跳出循环之后,根据结束时间 - 开始时间,计算出三个任务并发执行的总耗时。执行一下上述的单元测试,可以看到如下结果:开始做任务一 开始做任务二 开始做任务三 完成任务三,耗时:37毫秒 完成任务二,耗时:3661毫秒 完成任务一,耗时:7149毫秒 任务全部完成,总耗时:8025毫秒可以看到,通过异步调用,让任务一、二、三并发执行,有效的减少了程序的总运行时间。代码示例本文的相关例子可以查看下面仓库中的chapter4-1-2目录:Github:https://github.com/dyc87112/SpringBoot-LearningGitee:https://gitee.com/didispace/SpringBoot-Learning原文链接:https://blog.didispace.com/springbootasync/
  • [技术干货] java SpringBoot注解@Async不生效的解决方法
    方法一:同一个类中调用需要先获取代理对象,也就是手动获取对象12345678910111213141516171819@Service@EnableAsyncpublic class DemoService {    public void add(){        DemoService bean = SpringUtil.getBean(DemoService.class);        System.out.println("开始");        bean.sendToKafka();        System.out.println("结束");    }    @Async    public void sendToKafka() {        try {            Thread.sleep(10000);            System.out.println("我睡醒了!!!");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}方法二:不同的类调用,直接注入即可AsyncHandle.java (异步处理类)12345678910111213@Service@EnableAsyncpublic class AsyncHandle {    @Async    public void sendToKafka() {        try {            Thread.sleep(10000);            System.out.println("我睡醒了!!!");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}DemoService.java (业务类)1234567891011@Servicepublic class DemoService {    @Autowired    private AsyncHandle asyncHandle;         public void add(){        System.out.println("开始");        asyncHandle.sendToKafka();        System.out.println("结束");    }}总结:1、在需要用到的@Async注解的类上加上@EnableAsync,或者直接加在springboot启动类上2、异步处理方法(也就是加了@Async注解的方法)只能返回的是void或者Future类型3、同一个类中调用异步方法需要先获取代理类,因为@Async注解是基于Spring AOP (面向切面编程)的,而AOP的实现是基于动态代理模式实现的。有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。。。。。。这点很重要,也是经常遇到的原文链接:https://www.jb51.net/article/236337.htm
  • [技术干货] java set 包含_Java中Set的contains()方法
     本文主要讨论 集合Set 中存储对象的 hashCode 与 equals 方法应遵循的约束关系.新手对Set中contains()方法的疑惑 [java] 普通类会继承Object类的一切方法,但是不会重写Object类的方法 import java.util.HashSet; class Dog{ String color; public Dog(String s){ color = s; } } public class SetAndHashCode { public static void main(String[] args) { HashSet dogSet = new HashSet(); dogSet.add(new Dog('white')); dogSet.add(new Dog('white')); System.out.println('We have ' + dogSet.size() + ' white dogs!'); if(dogSet.contains(new Dog('white'))){ System.out.println('We have a white dog!'); }else{ System.out.println('No white dog!'); } } } 上述代码的输出为: [plain] We have 2 white dogs! No white dog! 程序中添加了两只白色的小狗到集合dogSet中. 且 size()方法显示有2只白色的小狗.但为什么用 contains()方法来判断时却提示没有白色的小狗呢? Set的contains(Object o) 方法详解 Java的API文档指出: 当且仅当 本set包含一个元素 e,并且满足(o==null ? e==null : o.equals(e))条件时,contains()方法才返回true. 因此 contains()方法 必定使用equals方法来检查是否相等. 需要注意的是: set 中是可以包含 null值的(常见的集合类都可以包含null值). 所以如果添加了null,然后判断是否包含null,将会返回true,代码如下所示: [java] HashSet a = new HashSet(); a.add(null); if(a.contains(null)){ System.out.println('true'); } Java的根类Object定义了 public boolean equals(Object obj) 方法.因此所有的对象,包括数组(array,[]),都实现了此方法。 在自定义类里,如果没有明确地重写(override)此方法,那么就会使用Object类的默认实现.即只有两个对象(引用)指向同一块内存地址(即同一个实际对象, x==y为true)时,才会返回true。 如果把Dog类修改为如下代码,能实现我们的目标吗? [java class Dog{ String color; public Dog(String s){ color = s; } //重写equals方法, 最佳实践就是如下这种判断顺序: public boolean equals(Object obj) { if (!(obj instanceof Dog)) return false; if (obj == this) return true; return this.color == ((Dog) obj).color; } } 英文答案是: no. 问题的关键在于 Java中hashCode与equals方法的紧密联系. hashCode() 是Object类定义的另一个基础方法.  equals()与hashCode()方法之间的设计实现原则为: 如果两个对象相等(使用equals()方法),那么必须拥有相同的哈希码(使用hashCode()方法). 即使两个对象有相同的哈希值(hash code),他们不一定相等.意思就是: 多个不同的对象,可以返回同一个hash值. hashCode()的默认实现是为不同的对象返回不同的整数.有一个设计原则是,hashCode对于同一个对象,不管内部怎么改变,应该都返回相同的整数值. 在上面的例子中,因为未定义自己的hashCode()实现,因此默认实现对两个对象返回两个不同的整数,这种情况破坏了约定原则。 解决办法 [java] class Dog{ String color; public Dog(String s){ color = s; } //重写equals方法, 最佳实践就是如下这种判断顺序: public boolean equals(Object obj) { if (!(obj instanceof Dog)) return false; if (obj == this) return true; return this.color == ((Dog) obj).color; } public int hashCode(){ return color.length();//简单原则 } } 但是上面的hashCode实现,要求Dog的color是不变的.否则会出现如下的这种困惑: [java] import java.util.HashSet; import java.util.Set; public class TestContains { public static final class Person{ private String name = ''; public Person(String n) { setName(n);  } public String getName() { return name; } public void setName(String name) { this.name = (name==null)? '' : name; } @Override public int hashCode() { // 请考虑是否值得这么做,因为此时name是会变的. return name.length(); // 推荐让name不可改变 } @Override public boolean equals(Object obj) { if(!(obj instanceof Person)){ return false; }  if(obj == this){ return true; } return this.name.equals(((Person)obj).name); } }; public static void main(String[] args) { Set persons = new HashSet();  // Person person = new Person('tiemao'); persons.add(person); // 修改name, 则依赖hash的集合可能失去作用 person.setName('ren'); // 同一个对象,居然是false,原因是我们重写了hashCode,打破了hashCode不变的基本约定 boolean has = persons.contains(person); int size = persons.size(); System.out.println('has='+has); // has=false. System.out.println('size='+size);// size=1 }  } ———————————————— 原文链接:https://blog.csdn.net/weixin_40007548/article/details/114090134 
  • [技术干货] Java中的List与Set转换
    一、List列表与Set列表的区别List列表是有序、可以重复、线程不安全的列表,Set是无序、不能重复、线程不安全的列表。但List和Set可以通过方法来转换为线程安全的,加互斥锁。Set set=new HashSet<>();// 转换为线程安全的集合Collections.synchronizedSet(set);List lists=new ArrayList<>();Collections.synchronizedList(lists);二、List与Set的转换1) List转换为setList ids=new ArrayList<>();HashSet set=new HashSet<>(ids);2) Set转换为ListHashSet set=new HashSet<>();List ids=new ArrayList<>(set);此处都可以使用 addAll()方法来替换。需要注意的是: List转换为Set的时候,当有重复数据时,转换为出现数据丢失的情况,因为Set集合不允许有重复数据。————————————————原文链接:https://blog.csdn.net/qq_33036061/article/details/103968822
  • No task executor bean found for async processing: no bean of type TaskExecut
    使用springcloud,添加异步方法后,调用异步成功,但有个 No task executor bean found for async processing: no bean of type TaskExecut 的异常抛出。经过试验,确定是因为没有配置异步线程池导致的。即使未配置,异步任务任然是成功的,可能使用的默认设置。解决办法是添加一个配置类,代码如下所示:@Configuration public class AsyncConfig { @Bean public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); // 设置核心线程数 executor.setMaxPoolSize(10); // 设置最大线程数 executor.setQueueCapacity(20); // 设置队列容量 executor.setKeepAliveSeconds(60); // 设置线程活跃时间(秒) executor.setThreadNamePrefix("user-rpt-"); // 设置默认线程名称 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  // 设置拒绝策略 executor.setWaitForTasksToCompleteOnShutdown(true); // 等待所有任务结束后再关闭线程池 return executor; } }原文链接:https://www.cnblogs.com/xsbx/p/10577409.html
  • Java中利用Alibaba开源技术EasyExcel来操作Excel表的示例代码
    1.Excel表格示例2.对象示例123456@Datapublic class DemoData { private String string; private Date date; private Double doubleData;}3.监听器(重点部分)123456789101112131415161718192021222324252627282930313233343536373839// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去public class DemoDataListener extends AnalysisEventListener<DemoData> { private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); /**  * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收  */ private static final int BATCH_COUNT = 5; List<DemoData> list = new ArrayList<DemoData>(); /**  * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。  */ private DemoDAO demoDAO; public DemoDataListener() {  // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数  demoDAO = new DemoDAO(); } /**  * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来  *  * @param demoDAO  */ public DemoDataListener(DemoDAO demoDAO) {  this.demoDAO = demoDAO; } /**  * 这个每一条数据解析都会来调用  *  * @param data  *   one row value. Is is same as {@link AnalysisContext#readRowHolder()}  * @param context  */ @Override public void invoke(DemoData data, AnalysisContext context) {  LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));  list.add(data);  // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM  if (list.size() >= BATCH_COUNT) {   saveData();   // 存储完成清理 list   list.clear();  } } /**  * 所有数据解析完成了 都会来调用  *  * @param context  */ @Override public void doAfterAllAnalysed(AnalysisContext context) {  // 这里也要保存数据,确保最后遗留的数据也存储到数据库  saveData();  LOGGER.info("所有数据解析完成!"); } /**  * 加上存储数据库  */ private void saveData() {  LOGGER.info("{}条数据,开始存储数据库!", list.size());  demoDAO.save(list);  LOGGER.info("存储数据库成功!"); }}4.持久层-将读出的Excel表格内容存到数据库12345678/** * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。 **/public class DemoDAO { public void save(List<DemoData> list) {  // 这里根据自己的业务需求自己去实现业务逻辑dao service }}5.测试类1234567891011121314151617181920/**  * 最简单的读  * <p>1. 创建excel对应的实体对象 参照{@link DemoData}  * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}  * <p>3. 直接读即可  */ @Test public void simpleRead() {  // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去  // 写法1:  String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";  // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭  EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();  // 写法2:  fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";  ExcelReader excelReader = null;  try {   excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();   ReadSheet readSheet = EasyExcel.readSheet(0).build();   excelReader.read(readSheet);  } finally {   if (excelReader != null) {    // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的    excelReader.finish();   }  } }5.测试结果原文链接:https://www.jb51.net/article/207878.htm
  • 2022年最后一次华为移动应用开发师资培训结束啦,来年再聚!
    HarmonyOS作为划时代的万物互联操作系统,为消费者带来跨终端无缝协同体验,同时华为全面开放HMS,使全球开发者便捷、快速地接入HMS生态进行应用创新,实现生态共享,开启移动应用开发新篇章。为促进高校进一步了解和学习移动应用开发技术,2022年陆续在2月、5月、暑期和11月举办全国范围的移动应用开发师资培训,来自全国各省市356所学校超千名教师报名参加。2022年11月14日—18日,2022年最后一次华为移动应用开发师资培训在线上如期举行。来自全国各省市58所院校、150位老师报名参加,培训考核通过率95%,其中数十位老师将继续开启移动应用开发者认证学习。开课仪式上,华为云高校生态运营总监宁利智进行“华为开放能力,助力创新实践型人才培养”为主题的演讲,重点介绍了华为云、HMS Core应用生态和HarmonyOS软硬件生态。倡导“技术科普、互动体验、动手实操”多元化的方式,培养学生的动手实践和创新能力;希望通过以下方式助力创新实践型人才培养:提供全方位师资培训,助力“双师”队伍建设;支持职业技能认证,助力院校培养复合型人才;精确匹配人才双方供给,助力院校毕业生实现高质量就业;举办丰富科创活动,打造人才交流平台;生态联运,提供代金券和奖学金支持等,赋能产教融合。本次培训详细讲解了华为移动应用开发所包含的课程体系架构和核心知识模块。课程内容包括HarmonyOS开发、HMS服务集成及综合案例讲解,为使大家更加充分了解鸿蒙相关知识点,特别增加了OpenHarmony硬件开发部分内容。通过理论授课结合实操演练的方式,帮助院校教师了解课程理论知识,掌握并熟练应用实践技能,扎实掌握课程的核心内容,为院校高质量开展鸿蒙相关板块教学工作做好赋能。
  • [技术干货] Java知识点--IO流(上)-转载
     一、文件 1、文件的含义 文件是保存数据的地方,比如word文档,txt文本文件,视频,图片等都是文件。  2、文件流 文件在程序中是以流的形式来操作的。 输入流:数据从文件到程序(内存)的路径。(在程序中读取文件数据) 输出流:数据从程序(内存)到文件的路径。(在程序中将数据写入文件)  二、常用的文件操作 1、创建文件对象相关构造器和方法 相关构造器: new File(String filePath) 根据路径创建File对象 new File(File parent,String child) 根据父目录文件+子路径创建File对象 new File(tring parent,String child) 根据父目录+子路径创建File对象 方法: createNewFile 创建新文件  2、创建文件案例演示(三种创建方法) import org.junit.jupiter.api.Test;  import java.io.File; import java.io.IOException;  public class FileCreate {     public static void main(String[] args) {      }      @Test     public void create01() {         String filePath = "e:\\news1.txt";         File file = new File(filePath);         try {             file.createNewFile();             System.out.println("文件创建成功");         } catch (IOException e) {             e.printStackTrace();         }     }      @Test     public void create02() {         File parentFile = new File("e:\\");         String fileName = "news2.txt";         File file = new File(parentFile, fileName);         try {             file.createNewFile();             System.out.println("文件创建成功");         } catch (IOException e) {             e.printStackTrace();         }     }      @Test     public void create03() {         String parentPath = "e:\\";         String fileName = "news3.txt";         File file = new File(parentPath, fileName);         try {             file.createNewFile();             System.out.println("文件创建成功");         } catch (IOException e) {             e.printStackTrace();         }     } } 3、获取文件相关信息的方法 “文件名字” — getName() “文件绝对路径” — getAbsolutePath() “文件父级目录” — getParent() “文件大小(字节)” — length() “文件是否存在” — exists() “是不是一个文件” — isFile() “是不是一个目录” — isDirectory()  4、获取文件相关信息方法案例演示 import org.junit.jupiter.api.Test;  import java.io.File;  public class FileInformation {     public static void main(String[] args) {      }      @Test     public void info(){         File file = new File("e:\\news1.txt");         System.out.println("文件名字" + file.getName());         System.out.println("文件绝对路径" + file.getAbsolutePath());         System.out.println("文件父级目录" + file.getParent());         System.out.println("文件大小(字节)" + file.length());         System.out.println("文件是否存在" + file.exists());         System.out.println("是不是一个文件" + file.isFile());         System.out.println("是不是一个目录" + file.isDirectory());     } } 5、目录的操作与删除 mkdir():创建一级目录 mkdirs():创建多级目录 delete():删除目录或文件  6、应用案例演示 (1)判断e:\news1.txt是否存在,如果存在就删除 (2)判断d:\demo02是否存在,如果存在就删除,否则提示不存在 (3)判断d:\demo\a\b\c是否存在,如果存在就提示存在,否则创建  import org.junit.jupiter.api.Test;  import java.io.File;  public class Directory_ {     public static void main(String[] args) {      }      @Test     public void m1() {         String pathName = "e:\\news1.txt";         File file = new File(pathName);         if (file.exists()) {             if (file.delete()) {                 System.out.println("删除成功");             } else {                 System.out.println("删除失败");             }         } else {             System.out.println("该文件不存在");         }     }      @Test     public void m2() {         String pathName = "d:\\demo02";         File file = new File(pathName);         if (file.exists()) {             if (file.delete()) {                 System.out.println("删除成功");             } else {                 System.out.println("删除失败");             }         } else {             System.out.println("该目录不存在");         }     }      @Test     public void m3() {         String directoryPath = "d:\\demo\\a\\b\\c";         File file = new File(directoryPath);         if (file.exists()) {             System.out.println(directoryPath + "已经存在");         } else {             if (file.mkdirs()) {                 System.out.println(directoryPath + "创建成功");             } else {                 System.out.println(directoryPath + "创建失败");             }         }     } }  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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 三、IO 流原理及流的分类 1、Java IO 流原理 (1) I/O是Input/Output的缩写,l/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。 (2) Java程序中,对于数据的输入/输出操作以”流(stream)”的方式进行。 (3) java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据。 (4) 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。 (5) 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。  2、流的分类 按操作数据单位不同分为:字节流(8 bit)二进制文件,字符流(按字符)文本文件 按数据流的流向不同分为:输入流,输出流 按流的角色的不同分为:节点流,处理流/包装流  (抽象基类)    字节流    字符流 输入流    InputStream    Reader 输出流    OutputStream    Writer 1) Java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的。 2)由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。  四、IO 流体系图  五、FileInputStream 字节输入流 可以用单个字节来读取文件,也可以用字节数组来读取文件。 以字节数组为例:  1、确定文件路径 String filePath = “e:\hello.txt”;  2、引入FileInputStream FileInputStream fileInputStream = new FileInputStream(filePath);  3、定义字节数组 byte[] buf = new byte[8];  4、定义读取字节数 int data = 0;  5、读取数据 while ((data = fileInputStream.read(buf)) != -1) {     System.out.println(new String(buf, 0, data)); } 1 2 3 用while循环读取,条件 = -1时说明读取完毕  6、关闭流 fileInputStream.close();  7、完整版 public void readFile02() {         String filePath = "e:\\hello.txt";         FileInputStream fileInputStream = null;         byte[] buf = new byte[8];         int data = 0;         try {             fileInputStream = new FileInputStream(filePath);             while ((data = fileInputStream.read(buf)) != -1) {                 System.out.println(new String(buf, 0, data));             }         } catch (IOException e) {             e.printStackTrace();         } finally {             try {                 fileInputStream.close();             } catch (IOException e) {                 e.printStackTrace();             }         }     }  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 六、FileOutputStream 字节输出流 可以用单个字节来写入文件,也可以用字节数组来写入文件。 以字节数组为例:  1、确定文件路径 String filePath = “e:\a.txt”;  2、引入FileOutputStream FileOutputStream fileOutputStream = new FileOutputStream(filePath);//运行时会覆盖上一次的结果 FileOutputStream fileOutputStream = new FileOutputStream(filePath, true); //运行时不会覆盖上一次的结果  3、写入数据 String str = “hello,world!”; fileOutputStream.write(str.getBytes());  4、关闭流 fileOutputStream.close();  5、完整版     public void writeFile() {         String filePath = "e:\\a.txt";         FileOutputStream fileOutputStream = null;         try {             //运行时会覆盖上一次的结果             fileOutputStream = new FileOutputStream(filePath);             //运行时不会覆盖上一次的结果             fileOutputStream = new FileOutputStream(filePath, true);             //写入单个字符             fileOutputStream.write('H');             //写入字符串             String str = "hello,world!";             //将字符串转成字节数组             fileOutputStream.write(str.getBytes());             //控制写入字符串范围             fileOutputStream.write(str.getBytes(), 0, str.length());         } catch (IOException e) {             e.printStackTrace();         } finally {             try {                 fileOutputStream.close();             } catch (IOException e) {                 e.printStackTrace();             }         }     }  七、用字节流拷贝文件 1、确定原文件与新文件路径 String filePath = “e:\1.jpg”; String newFilePath = “d:\1.jpg”;  2、引入FileInputStream 与 FileOutputStream FileInputStream fileInputStream = new FileInputStream(filePath); fileOutputStream fileOutputStream = new FileOutputStream(newFilePath);  3、定义字节数组 byte[] buf = new byte[1024];  4、定义读取字节数 int data = 0;  5、拷贝数据 while ((data = fileInputStream.read(buf)) != -1) {     fileOutputStream.write(buf, 0, data); } 6、关闭流 fileInputStream.close(); fileOutputStream.close();  7、完整版 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;  public class FileCopy {     public static void main(String[] args) {         String filePath = "e:\\1.jpg";         String newFilePath = "d:\\1.jpg";         FileInputStream fileInputStream = null;         FileOutputStream fileOutputStream = null;         try {             fileInputStream = new FileInputStream(filePath);             fileOutputStream = new FileOutputStream(newFilePath);             byte[] buf = new byte[1024];             int data = 0;             while ((data = fileInputStream.read(buf)) != -1) {                 fileOutputStream.write(buf, 0, data);             }             System.out.println("拷贝成功");         } catch (IOException e) {             e.printStackTrace();         } finally {             try {                 if (fileInputStream != null){                     fileInputStream.close();                 }                 if (fileOutputStream != null){                     fileOutputStream.close();                 }             } catch (IOException e) {                 e.printStackTrace();             }         }     } } 八、FileReader 字符输入流 可以用单个字符来读取文件,也可以用字符数组来读取文件。  1、FileReader 相关方法: (1) new FileReader(File/String) (2) read:每次读取单个字符,返回该字符,如果到文件末尾返回-1 (3) read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1 相关API: (1) new String(char[]):将char[]转换成String (2) new String(char[],off,len):将char[]的指定部分转换成String  2、以字符数组来读取文件 import java.io.FileReader; import java.io.IOException;  public class FileReader_ {     public static void main(String[] args) {         String filePath = "e:\\story.txt";         FileReader fileReader = null;         char[] chars = new char[100];         int data = 0;         try {             fileReader = new FileReader(filePath);             while ((data = fileReader.read(chars)) != -1) {                 System.out.print(new String(chars, 0, data));             }         } catch (IOException e) {             e.printStackTrace();         } finally {             try {                 fileReader.close();             } catch (IOException e) {                 e.printStackTrace();             }         }     } } 九、FileWriter 字符输入流 1、 FileWriter 常用方法 1) new FileWriter(File/String):覆盖模式,相当于流的指针在首端 2) new FileWriter(File/String,true):追加模式,相当于流的指针在尾端 3) write(int):写入单个字符 4) write(char[]):写入指定数组 5) write(char[],off,len):写入指定数组的指定部分 6) write(string):写入整个字符串 7) write(string,off,len):写入字符串的指定部分 相关API: String类: toCharArray:将String转换成char[] >注意: FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!  2、写入文件案例 import java.io.FileWriter; import java.io.IOException;  public class FileWriter_ {     public static void main(String[] args) {         String filePath = "e:\\1.txt";         FileWriter fileWriter = null;         try {             fileWriter = new FileWriter(filePath);             fileWriter.write('H');              char[] chars = {'a', 'b', 'c'};             fileWriter.write(chars);              fileWriter.write("大事发生".toCharArray(), 0, 2);              fileWriter.write("巅峰时代");              fileWriter.write("法国电视", 0, 3);         } catch (IOException e) {             e.printStackTrace();         } finally {             try {                 fileWriter.close();             } catch (IOException e) {                 e.printStackTrace();             }         }     } }  ———————————————— 版权声明:本文为CSDN博主「邱崇源」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/programmerchiu/article/details/127688980 
  • [技术干货] 【JAVA高级】——吃透JDBC中的事务及事务的封装-转载
     ✨ service层控制事务 package com.cxyzxc.examples07;  import java.sql.Connection; import java.sql.SQLException;  public class AccountServiceImpl {      /**      * 转账业务      *       * @param fromNo      *            转账人账号      * @param password      *            转账人账号密码      * @param toNo      *            收款人账号      * @param money      *            转账金额      */     public String transfer(String fromNo, String password, String toNo,             double money) {         String result = "转账失败";         AccountDaoImpl accountDaoImpl = new AccountDaoImpl();          // 创建一个连接对象         Connection connection = null;          try {             // 获取连接对象             connection = DBUtils.getConnection();             // 开启事务,关闭事务的自动提交,改为手动提交             connection.setAutoCommit(false);             // 1.验证fromNo账号是否存在             Account fromAccount = accountDaoImpl.select(fromNo);             if (fromAccount == null) {                 throw new RuntimeException("卡号不存在");             }              // 2.验证fromNo的密码是否正确             if (!fromAccount.getPassword().equals(password)) {                 throw new RuntimeException("密码错误");             }              // 3.验证余额是否充足             if (fromAccount.getBalance() < money) {                 throw new RuntimeException("余额不足");             }              // 4.验证toNo账号是否存在             Account toAccount = accountDaoImpl.select(toNo);             if (toAccount == null) {                 throw new RuntimeException("对方卡号不存在");             }             // 5.减少fromNo账号的余额             fromAccount.setBalance(fromAccount.getBalance() - money);             accountDaoImpl.update(fromAccount);              // 程序出现异常             int num = 10 / 0;              // 6.增加toNo账号的余额             toAccount.setBalance(toAccount.getBalance() + money);             accountDaoImpl.update(toAccount);             // 代码执行到这里,说明转账成功,提交事务             connection.commit();             result = "转账成功";             return result;         } catch (Exception e) {             e.printStackTrace();             try {                 // 出现异常,回滚整个事务                 System.out.println("出现异常,回滚整个事务,转账失败");                 connection.rollback();             } catch (SQLException e1) {                 e1.printStackTrace();             }         }finally{             DBUtils.closeAll(connection, null, null);         }         return result;     } } ✨ service层控制事务失败的原因 执行这个代码,观察account表中的数据发现,当程序出现异常,转账账号余额减少了,但是收款账户余额没有增加,事务控制失败了。失败的原因是:  AccountServiceImpl类中的connection连接对象与AccountDaoImpl类中给各个方法里的connection连接对象是不同的connection连接对象。 AccountServiceImpl类中的connection连接对象控制事务,只能控制AccountServiceImpl类中的事务,不能控制该类之外的类里的事务  ✨ 解决方案一:传递Connection 为了解决AccountServiceImpl类中的connection连接对象与AccountDaoImpl类中给各个方法里的connection连接对象是不同步的问题,可以将Connection对象通过service传递给AccountDaoImpl类中的各个方法  💫 AccountDaoImpl类代码 AccountDaoImpl类中的每个方法参数列表里都要添加一个Connection类型的参数  package com.cxyzxc.examples08;  import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List;  public class AccountDaoImpl {      // 新增:插入一个Account对象到数据库中     public int insert(Account account,Connection connection) {         PreparedStatement preparedStatement = null;          String sql = "insert into account values(?,?,?,?)";          try {             preparedStatement = connection.prepareStatement(sql);              // 绑定参数             preparedStatement.setString(1, account.getCardNo());             preparedStatement.setString(2, account.getPassword());             preparedStatement.setString(3, account.getName());             preparedStatement.setDouble(4, account.getBalance());              // 执行SQL             int result = preparedStatement.executeUpdate();             return result;         } catch (SQLException e) {             e.printStackTrace();         }          return 0;     }      // 删除:根据卡号,删除账号     public int delete(String cardNo,Connection connection) {         PreparedStatement preparedStatement = null;          String sql = "delete from account where cardNo = ?;";         try {             preparedStatement = connection.prepareStatement(sql);             // 绑定参数             preparedStatement.setString(1, cardNo);             // 执行SQL             int result = preparedStatement.executeUpdate();             return result;         } catch (SQLException e) {             e.printStackTrace();         }          return 0;     }      // 修改     public int update(Account account,Connection connection) {         PreparedStatement preparedStatement = null;          String sql = "update account set password = ?,name = ?,balance = ? where cardNo=?;";          try {             preparedStatement = connection.prepareStatement(sql);              // 绑定参数             preparedStatement.setString(1, account.getPassword());             preparedStatement.setString(2, account.getName());             preparedStatement.setDouble(3, account.getBalance());             preparedStatement.setString(4, account.getCardNo());              // 执行SQL             int result = preparedStatement.executeUpdate();             return result;         } catch (SQLException e) {             e.printStackTrace();         }         return 0;     }      // 查询单个     public Account select(String cardNo,Connection connection) {         PreparedStatement preparedStatement = null;         ResultSet resultSet = null;         Account account = null;          String sql = "select * from account where cardNo = ?";          try {             preparedStatement = connection.prepareStatement(sql);              // 绑定参数             preparedStatement.setString(1, cardNo);              // 执行SQL             resultSet = preparedStatement.executeQuery();              if (resultSet.next()) {                 String cardNumber = resultSet.getString("cardNo");                 String password = resultSet.getString("password");                 String name = resultSet.getString("name");                 double balance = resultSet.getDouble("balance");                  account = new Account(cardNumber, password, name, balance);             }              return account;         } catch (SQLException e) {             e.printStackTrace();         }         return null;     }      // 查询所有     public List<Account> selectAll(Connection connection) {         PreparedStatement preparedStatement = null;         ResultSet resultSet = null;         Account account = null;         List<Account> accountList = new ArrayList<Account>();          String sql = "select * from account;";          try {             preparedStatement = connection.prepareStatement(sql);              // 执行SQL             resultSet = preparedStatement.executeQuery();              while (resultSet.next()) {                 String cardNumber = resultSet.getString("cardNo");                 String password = resultSet.getString("password");                 String name = resultSet.getString("name");                 double balance = resultSet.getDouble("balance");                  account = new Account(cardNumber, password, name, balance);                 accountList.add(account);             }             return accountList;         } catch (SQLException e) {             e.printStackTrace();         }         return null;     } } 💫 AccountServiceImpl类代码 package com.cxyzxc.examples08;  import java.sql.Connection; import java.sql.SQLException;  public class AccountServiceImpl {      /**      * 转账业务      *       * @param fromNo      *            转账人账号      * @param password      *            转账人账号密码      * @param toNo      *            收款人账号      * @param money      *            转账金额      */     public String transfer(String fromNo, String password, String toNo,             double money) {         String result = "转账失败";         AccountDaoImpl accountDaoImpl = new AccountDaoImpl();          // 创建一个连接对象         Connection connection = null;          try {             // 获取连接对象             connection = DBUtils.getConnection();             // 开启事务,关闭事务的自动提交,改为手动提交             connection.setAutoCommit(false);             // 1.验证fromNo账号是否存在             Account fromAccount = accountDaoImpl.select(fromNo,connection);             if (fromAccount == null) {                 throw new RuntimeException("卡号不存在");             }              // 2.验证fromNo的密码是否正确             if (!fromAccount.getPassword().equals(password)) {                 throw new RuntimeException("密码错误");             }              // 3.验证余额是否充足             if (fromAccount.getBalance() < money) {                 throw new RuntimeException("余额不足");             }              // 4.验证toNo账号是否存在             Account toAccount = accountDaoImpl.select(toNo,connection);             if (toAccount == null) {                 throw new RuntimeException("对方卡号不存在");             }             // 5.减少fromNo账号的余额             fromAccount.setBalance(fromAccount.getBalance() - money);             accountDaoImpl.update(fromAccount,connection);              // 程序出现异常             int num = 10 / 0;              // 6.增加toNo账号的余额             toAccount.setBalance(toAccount.getBalance() + money);             accountDaoImpl.update(toAccount,connection);             // 代码执行到这里,说明转账成功,提交事务             connection.commit();             result = "转账成功";             return result;         } catch (Exception e) {             e.printStackTrace();             try {                 // 出现异常,回滚整个事务                 System.out.println("出现异常,回滚整个事务,转账失败");                 connection.rollback();             } catch (SQLException e1) {                 e1.printStackTrace();             }         }finally{             DBUtils.closeAll(connection, null, null);         }         return result;     } } 💫 测试 测试发现,这种方法可以解决service层控制事务失败的问题。  💫 解决方案的弊端 (1)如果使用传递Connection,更容易造成接口污染(BadSmell)。  (2)定义接口是为了更容易更换实现,而将Connection定义在接口中(XxxDao接口,XXXDaoImpl实现XxxDao接口)中,会造成污染当前接口。因为在当前代码中连接对象叫Connection,而在其它数据库连接框架中,连接对象不叫Connection(Mybatis框架中数据库连接对象叫SqlSession,Hibernate框架中数据库连接对象叫session),这时候,你需要重新定义接口,重新传递连接对象。  ✨ 解决方案二:ThreadLocal (1)在整个线程中(单线程),存储一个共享值(Connection对象)。  (2)线程类中拥有一个类似Map的属性(),以键值对的结构<ThreadLocal对象,值>存储数据。  💫 ThreadLocal应用 一个线程中所有的操作共享一个ThreadLocal,ThreadLocal里存储的是Connection连接对象,在整个操作流程中任何一个操作环节都可以设置或者获取值。   💫 ThreadLocal代码实现 在DBUtils类中,将当前Connection对象添加到ThreadLocal中。其它类代码保持不变。  package com.cxyzxc.examples09;  import java.sql.*;  public class DBUtils {     // 声明一个ThreadLocal<Connection>对象用来存储数据库连接对象     private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();      static {// 类加载,执行一次!         try {             Class.forName("com.mysql.jdbc.Driver");         } catch (ClassNotFoundException e) {             e.printStackTrace();         }     }      // 1.获取连接     public static Connection getConnection() {         // 将当前线程中绑定的Connection对象赋值给connection变量         Connection connection = threadLocal.get();         try {             // 如果连接对象为null,则创建一个连接对象             if (connection == null) {                 connection = DriverManager.getConnection(                         "jdbc:mysql://localhost:3306/java22182", "root",                         "123456");                 // 将创建的连接对象存储到当前线程中共享                 threadLocal.set(connection);             }          } catch (SQLException e) {             e.printStackTrace();         }         return connection;     }      // 2.释放资源     public static void closeAll(Connection connection, Statement statement,             ResultSet resultSet) {         try {             if (resultSet != null) {                 resultSet.close();             }             if (statement != null) {                 statement.close();             }             if (connection != null) {                 connection.close();                 // 将connection从threadLocal中移除                 threadLocal.remove();             }         } catch (SQLException e) {             e.printStackTrace();         }     } } 💖 事务的封装 ✨ 问题描述 (1)XXXDaoImpl类是专门用来操作数据表的,在这个类中只存在对数据表增删改查的方法,没有其它的内容。这是我们需要的。但是在XXXServiceImpl类中,既有业务需求,还有获取数据库连接对象以及释放资源的代码,在XXXServiceImpl类中,应该只有业务逻辑需求,除此,没有其它操作代码,这才是我们需要的。  (2)因此我们需要将对事务的开启、提交、回滚都封装到DBUtils工具类中。业务层调用DBUtils类中的与事务相关的代码即可。  ✨ 完善工具类 package com.cxyzxc.examples10;  import java.sql.*;  public class DBUtils {     // 声明一个ThreadLocal<Connection>对象用来存储数据库连接对象     private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();      static {// 类加载,执行一次!         try {             Class.forName("com.mysql.jdbc.Driver");         } catch (ClassNotFoundException e) {             e.printStackTrace();         }     }      // 1.获取连接     public static Connection getConnection() {         // 将当前线程中绑定的Connection对象赋值给connection变量         Connection connection = threadLocal.get();         try {             // 如果连接对象为null,则创建一个连接对象             if (connection == null) {                 connection = DriverManager.getConnection(                         "jdbc:mysql://localhost:3306/java22182", "root",                         "123456");                 // 将创建的连接对象存储到当前线程中共享                 threadLocal.set(connection);             }          } catch (SQLException e) {             e.printStackTrace();         }         return connection;     }      // 2.释放资源     public static void closeAll(Connection connection, Statement statement,             ResultSet resultSet) {         try {             if (resultSet != null) {                 resultSet.close();             }             if (statement != null) {                 statement.close();             }             if (connection != null) {                 connection.close();                 // 将connection从threadLocal中移除                 threadLocal.remove();             }         } catch (SQLException e) {             e.printStackTrace();         }     }      // 3、开启事务     public static void startTransaction() {         Connection connection = null;         try {             connection = getConnection();             connection.setAutoCommit(false);         } catch (SQLException e) {             e.printStackTrace();         }     }      // 4、提交事务     public static void commitTransaction() {         Connection connection = getConnection();         try {             connection.commit();         } catch (SQLException e) {             e.printStackTrace();         } finally {             DBUtils.closeAll(connection, null, null);         }     }      // 5、回滚事务     public static void rollbackTransaction() {         Connection connection = getConnection();         try {             connection.rollback();         } catch (SQLException e) {             e.printStackTrace();         } finally {             DBUtils.closeAll(connection, null, null);         }     } } ✨ AccountServiceImpl类代码修改 package com.cxyzxc.examples10;  public class AccountServiceImpl {      /**      * 转账业务      *       * @param fromNo      *            转账人账号      * @param password      *            转账人账号密码      * @param toNo      *            收款人账号      * @param money      *            转账金额      */     public String transfer(String fromNo, String password, String toNo,             double money) {         String result = "转账失败";         AccountDaoImpl accountDaoImpl = new AccountDaoImpl();          try {              // 开启事务,关闭事务的自动提交,改为手动提交             DBUtils.startTransaction();             // 1.验证fromNo账号是否存在             Account fromAccount = accountDaoImpl.select(fromNo);             if (fromAccount == null) {                 throw new RuntimeException("卡号不存在");             }              // 2.验证fromNo的密码是否正确             if (!fromAccount.getPassword().equals(password)) {                 throw new RuntimeException("密码错误");             }              // 3.验证余额是否充足             if (fromAccount.getBalance() < money) {                 throw new RuntimeException("余额不足");             }              // 4.验证toNo账号是否存在             Account toAccount = accountDaoImpl.select(toNo);             if (toAccount == null) {                 throw new RuntimeException("对方卡号不存在");             }             // 5.减少fromNo账号的余额             fromAccount.setBalance(fromAccount.getBalance() - money);             accountDaoImpl.update(fromAccount);              // 程序出现异常             @SuppressWarnings("unused")             int num = 10 / 0;              // 6.增加toNo账号的余额             toAccount.setBalance(toAccount.getBalance() + money);             accountDaoImpl.update(toAccount);             // 代码执行到这里,说明转账成功,提交事务             DBUtils.commitTransaction();             result = "转账成功";             return result;         } catch (Exception e) {             e.printStackTrace();             // 出现异常,回滚整个事务             System.out.println("出现异常,回滚整个事务,转账失败");             DBUtils.rollbackTransaction();         }         return result;     } } ———————————————— 版权声明:本文为CSDN博主「Java Fans」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/hh867308122/article/details/127636114 
  • 【JavaSE】Java中的异常那些事儿-转载
     一. 异常的概念与体系结构 ✔️1.1 异常的概念 在Java中,将程序执行过程中发生的不正常行为称为异常  比如我们前面接触过的几种异常  算术异常 public static void main1(String[] args) {     int a = 10/0 ;     System.out.println(a); } 空指针异常(引用=null) public static void main(String[] args) {     int[] array = null;     System.out.println(array.length);     System.out.println("hahahafafafa"); } 数组越界异常 public static void main(String[] args) {     int[] array = {1,2,3,4,5};     System.out.println(array[10]);     System.out.println("hahahahqqqppp"); } ✔️1.2 异常的体系结构 异常种类繁多,为了对不同异常或者错误进行很好的分类管理,Java内部维护了一个异常的体系结构: Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:StackOverflowError和OutOfMemoryError,一旦发生回力乏术,就像人得了癌症。 Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception。 栈溢出错误就是Error  public static void func(){     func(); }  public static void main(String[] args) {     func(); } ✔️1.3 异常的分类 异常可能在编译时发生,也可能在程序运行时发生,根据发生的时机不同,可以将异常分为:  编译时异常(受查异常) 在程序编译期间发生的异常,称为编译时异常,也称为受检查异常(Checked Exception)  例如我们之前接触到的克隆  class Person implements Cloneable{     @Override     protected Object clone() throws CloneNotSupportedException {         return super.clone();     } }  public static void main(String[] args) throws CloneNotSupportedException {     Person person = new Person();     Person person1 = (Person)person.clone(); } 此处可以看到我们main函数衔接了一个异常,有的人会不懂,别急,请看后文,如何操作呢,我们在IDEA中用Alt+回车+回车快捷键throws声明一个异常就可以解决程序编译报错的问题了(在报错处操作)  注意:少了等于号,分号,逗号等等的属于编译时语法错误,是编译错误,不属于异常  运行时异常(非受查异常) 在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception) RunTimeException以及其子类对应的异常,都称为运行时异常。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。 例如我们的数组越界:  public static void main(String[] args) {     int[] array = {1,2,3,4};     System.out.println(array[9]);     System.out.println("hahahaqqqppp"); } 代码写完之后系统并没有红线提示,而是在运行的时候报错了 注意:编译时出现的语法性错误,不能称之为异常。例如将 System.out.println 拼写错了, 写成了system.out.println. 此时编译过程中就会出错, 这是 “编译期” 出错。而运行时指的是程序已经编译通过得到class 文件了, 再由 JVM 执行过程中出现的错误.  🚩二. 异常的处理 ♻️ 2.1 防御式编程 例如一个进入游戏的操作 LBYL: Look Before You Leap. 在操作之前就做充分的检查. 即:事前防御型 public static void main7(String[] args) {     boolean ret = false;     ret = 登陆游戏();     if (!ret) {         处理登陆游戏错误;         return;     }     ret = 开始匹配();     if (!ret) {         处理匹配错误;         return;     }     ret = 游戏确认();     if (!ret) {         处理游戏确认错误;         return;     }     ret = 选择英雄();     if (!ret) {         处理选择英雄错误;         return;     }     ret = 载入游戏画面();     if (!ret) {         处理载入游戏错误;         return;     }     ...... } LBYL是将每一步操作的对错可能性都进行了处理  缺陷:正常流程和错误处理流程代码混在一起, 代码整体显的比较混乱。  EAFP: It’s Easier to Ask Forgiveness than Permission. “事后获取原谅比事前获取许可更容易”. 也就是先操作,遇到问题再处理. 即:事后认错型 public static void main(String[] args) {     try {         登陆游戏();         开始匹配();         游戏确认();         选择英雄();         载入游戏画面();         ...     } catch (登陆游戏异常) {         处理登陆游戏异常;     } catch (开始匹配异常) {         处理开始匹配异常;     } catch (游戏确认异常) {         处理游戏确认异常;     } catch (选择英雄异常) {         处理选择英雄异常;     } catch (载入游戏画面异常) {         处理载入游戏画面异常;     }     ......  } 优势:正常流程和错误流程是分离开的, 程序员更关注正常流程,代码更清晰,容易理解代码 所以异常处理的核心思想就是 EAFP。 在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws。 ♻️ 2.2 异常的抛出 在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。 在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:  public static void func1(int a){     if(a == 10){         throw new RuntimeException("a==10");     } }  public static void main(String[] args) {     func1(10); } 其中在func1方法里规定了当a等于10的时候,就抛出一个指定的异常对象,所以函数调用func1的时候,传10的参数就会执行指定的异常 注意: throw必须写在方法体内部 抛出的对象必须是Exception 或者 Exception 的子类对象 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理 如果抛出的是编译时异常,用户必须处理,否则无法通过编译 异常一旦抛出,其后的代码就不会执行 ♻️ 2.3 异常的捕获 异常的捕获,也就是异常的具体处理方式,主要有两种:异常声明throws 以及 try-catch捕获处理。  📐2.3.1 异常声明throws 处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常。 语法格式:  修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{ } 1 2 在方法中使用throws之后,在主函数里调用者必须要处理(后面讲解如何处理) //声明一下异常throws  但是此时没有处理异常 public static void func3(int a) throws CloneNotSupportedException {     if(a == 10){         throw new CloneNotSupportedException("a == 10");     } }  public static void main(String[] args) {     try{         func3(10);     }catch(CloneNotSupportedException e){         e.printStackTrace();     }     System.out.println("hahaha"); } 如果方法中没有声明异常,而是直接处理了异常,则在函数里可以直接调用 public static void func2(int a){     //处理异常     try{         if(a == 10){          throw new CloneNotSupportedException("a == 10");         }     }catch(CloneNotSupportedException e){         e.printStackTrace();     } }  public static void main(String[] args) {     func2(10); } 3.方法中处理了异常,在函数中又throws之后,此时还是没有处理异常,所以交给了JVM  public static void main(String[] args) throws CloneNotSupportedException{     func3(10);     System.out.println("haha"); }注意: throws必须跟在方法的参数列表之后 声明的异常必须是 Exception 或者 Exception 的子类 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出 📐2.3.2 try-catch捕获并处理 throws对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行处理,就需要try-catch。  语法格式:   public static void main(String[] args) {         try{             // 将可能出现异常的代码放在这里         }catch(要捕获的异常类型 e){             // 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型一致时,或者是try中抛出异常的基类时,就会被捕获到             // 对异常就可以正常处理,处理完成后,跳出try-catch结构,继续执行后序代码         }catch(异常类型 e){             // 对异常进行处理         }finally{             // 此处代码一定会被执行到         }         // 后序代码         // 当异常被捕获到时,异常就被处理了,这里的后序代码一定会执行         // 如果捕获了,由于捕获时类型不对,那就没有捕获到,这里的代码就不会被执行     } 当我们再拿出数组越界这段代码的时候可以看到没有处理异常,那么就会交给JVM来处理,此时程序会立即异常终止,code返回1  public static void main(String[] args) {     int[] array = {1,2,3,4,5};      System.out.println(array[10]);//此处没有处理异常,那么就会交给JVM来处理,此时程序会立即异常终止      System.out.println("其他业务逻辑"); } 我们使用try-catch捕获并处理  public static void main(String[] args) {     int[] array = {1,2,3,4,5};     try{         //System.out.println(array[10]);//此处没有处理异常,那么就会交给JVM来处理,此时程序会立即异常终止         func2(10);         System.out.println("hhhh");//只要上面发生了异常,后面的代码就不会执行了     }catch(ArrayIndexOutOfBoundsException e){//输入异常的类型,变量         e.printStackTrace();//打印当前异常的信息         System.out.println("捕获到了一个数组越界的异常!!可以发你一个邮件,告知你原因");     }     System.out.println("其他业务逻辑"); } 当然我们这里如果在try里面输入的数据发生了异常,但是在catch里面没有提及到相关异常的捕捉,那么程序依旧会运行失败。如果在try里面有多个异常,它只会捕获第一个异常,其余的就不执行了,程序当中,同时只能抛出一个异常。  关于异常的处理方式:  异常的种类有很多, 我们要根据不同的业务场景来决定. 对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果 对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿 对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试. 在我们当前的代码中采取的是经过简化的第二种方式. 我们记录的错误日志是出现异常的方法调用信息, 能很快速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息. 注意:  try块内抛出异常位置之后的代码将不会被执行 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序----异常是按照类型来捕获的 try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获 如果多个异常的处理方式是完全相同, 也可以写成这样:中间用|来衔接异常,变量名放最后 }catch(ArrayIndexOutOfBoundsException | NullPointerException e){//输入异常的类型,变量 1 如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误:  public static void main(String[] args) {   int[] array = {1,2,3,4,5};    try{        System.out.println(array[9]);        System.out.println("haha");    }catch (NullPointerException e){        System.out.println("空指针异常!");        e.printStackTrace();    }catch (ArrayIndexOutOfBoundsException e){        System.out.println("数组越界异常!");        e.printStackTrace();    }catch (Exception e){//Exception可以捕捉到所有的异常  兜底,必须放在最后        e.printStackTrace();    } } Exception是所有异常的父类,必须写在最后面, 因此可以用这个类型表示捕捉所有异常.  4. 可以通过一个catch捕获所有的异常,即多个异常,一次捕获(不推荐)  public static void main(String[] args) {    int[] array = {1,2,3,4,5};    try{        System.out.println(array[10]);        System.out.println("haha");    }catch (Exception e){//不建议这样写 具体的异常才能解决具体的问题        e.printStackTrace();    } } 只有具体的异常才能解决具体的问题,捕捉范围太大是不可控的  备注: catch 进行类型匹配的时候, 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类对象.如刚才的代码, NullPointerException 和 ArrayIndexOutOfBoundsException 都是 Exception 的子类, 因此都能被捕获到.  📐2.3.3 finally 在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的。 语法格式:  public static void main(String[] args) {     try{         // 可能会发生异常的代码     }catch(异常类型 e){         // 对捕获到的异常进行处理     }finally{         // 此处的语句无论是否发生异常,都会被执行到     }     // 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行 } 代码演练:输入一个数被10整除  public static void main(String[] args) {     Scanner scanner = new Scanner(System.in);     try{         int a = scanner.nextInt();         System.out.println(10/a);         System.out.println("abcdefg");     }catch(ArithmeticException e){         System.out.println("捕获到了算术异常");         e.printStackTrace();     }finally {         scanner.close();//关闭资源         System.out.println("一般用来关闭资源!");//不管是否发生异常,finally一定会被执行     }     System.out.println("其他业务逻辑"); } 没有发生异常的结果:  发生了异常的结果:  那有人问既然 finally 和 try-catch-finally 后的代码都会执行,那为什么还要有finally呢? 在一段程序中,如果正常输入,成功接收输入后程序就返回了,try-catch-finally之后的代码根本就没有执行,即输入流就没有被释放,造成资源泄漏。  在以下代码中:当try里面有计算的时候,还有return的时候,以及finally中也有return的时候,这时候我们程序会怎么输出呢?  public static int func(){     try{         System.out.println(10/10);         return 10;     }catch(ArithmeticException e){         System.out.println("这里捕捉到了算术异常");     }finally {         //return -1;     }     return 13; }  public static void main(String[] args) {     System.out.println(func()); } 输出的结果:  从这个结果中我们可以看到执行了try中的计算和finally中的return,个人理解是finally中的return的值覆盖了try里面的return的值,但恰巧这样的代码方式是不推荐的,我们尽量不要在finally语句中进行return(被编译器当做一个警告).。(finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.)如果在 try-catch-finally 后的代码中return一个数是不会发生异常的,输出就是try里的两个值。  注意:finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作。  在这里面就有面试题可问了:  throw 和 throws 的区别? ①:throw 是语句抛出一个异常;throws 是方法抛出一个异常 ②:throws可以单独使用,但throw不能 ③:throw要么和try-catch-finally语句配套使用,要么与throws配套使用。但throws可以单独使用,然后再由处理异常的方法捕获 finally中的语句一定会执行吗? finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作  ♻️ 2.4 异常的处理流程 关于 “调用栈” 方法之间是存在相互调用关系的, 这种调用关系我们可以用 “调用栈” 来描述. 在 JVM 中有一块内存空间称为 “虚拟机栈” 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈.  在方法里没有处理异常,就会交给main函数处理,如果main函数也没处理,就会交给JVM处理  异常处理流程总结:  程序先执行 try 中的代码 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配. 如果找到匹配的异常类型, 就会执行 catch 中的代码 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行) 如果上层调用者也没有处理的了异常, 就继续向上传递 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止  🚩三. 自定义异常类 Java 中虽然已经内置了丰富的异常类, 但是并不能完全表示实际开发中所遇到的一些异常,此时就需要维护符合我们实际情况的异常结构  如果要自定义一个异常,那么需要继承于一个异常类,一般我们推荐继承2个中的一个 1.Exception:默认就是一个受查异常,或者是一个编译时异常,必须"处理"之后才可以运行,使用try来处理,throws来声明 2. RuntimeException:非受查时异常,运行时异常  例如我们写一个登录类:  在登陆类里我们只需要判断用户名和密码即可,此时我们在处理用户名密码错误的时候可能就需要抛出两种异常. 我们可以基于已有的异常类进行扩展(继承), 创建和我们业务相关的异常类 定义用户名异常类: ①我们自定义抛出RuntimeException异常,再定义一个类继承RuntimeException并让throws抛出它 定义密码类异常: ①我们自定义抛出RuntimeException异常,再定义一个类继承RuntimeException并让throws抛出它  此时可以把两种异常结合起来,在try里输入名字密码,在if语句中判断,为了能让异常名是符合我们需求的,而不是系统自带的异常名,我们需要在语句抛出异常throw中传参,传我们自己想定义的异常名字,传参的话此时UserNameErrorException类和PasswordErrorException类里需要完善代码来接收字符串,总的代码如下:  class LogIn {     private String userName = "admin";     private String Keyword = "111111";     public void loginInfo(String userName, String Keyword) throws RuntimeException{         if (!this.userName.equals(userName)) {             System.out.println("用户名错误!");             throw new UserNameErrorException("我是用户名错误的异常的参数");         }         if (!this.word.equals(Keyword)) {             System.out.println("密码错误!");             throw new wordErrorException("我是密码错误的异常的参数");         }         System.out.println("登陆成功");     }      public static void main(String[] args) {       try{           LogIn logIn = new LogIn();           logIn.loginInfo("admin888", "111111");       }catch (UserNameErrorException errorException){           errorException.printStackTrace();           System.out.println("用户名异常!");       }catch (KeywordErrorException e){           e.printStackTrace();           System.out.println("密码异常!");       }     } }  用户名类:  public class UserNameErrorException extends RuntimeException{     public UserNameErrorException() {         super();     }      public UserNameErrorException(String message) {         super(message);     } } 密码类:  public class KeywordErrorException extends RuntimeException{     public KeywordErrorException() {         super();     }      public wordErrorException(String message) {         super(message);     } } ———————————————— 版权声明:本文为CSDN博主「bit me」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_67660672/article/details/127460707 
  • [技术干货] 基于MATLAB的指纹识别算法仿真实现-转载
    一、理论基础        在指纹图像预处理部分,论文对预处理的各个步骤包括规格化、图像分割、中值滤波、二值化、细化等以及各个步骤的方法进行了深入的分析和研究,选择了一种图像预处理方案。在指纹特征提取部分,采用基于Matlab实现的指纹细节特征提取方法,并给出了去伪算法。指纹特征提取是从细化后的指纹图中得到细节特征点(即端点和分叉点),此特征点含有大量的伪特征,既耗时又影响匹配精度。采用了边缘去伪和距离去伪,使得特征点去伪前后减小了近1/3,然后提取可靠特征点信息,以便实现指纹匹配。       指纹识别技术是指使用取像设备读取指纹图像,通过识别软件提取出指纹图像中的特征数据,然后根据匹配算法得到的结果鉴别指纹所有人身份的生物特征识别技术。         指纹识别系统主要涉及三大步骤:指纹图像预处理、特征提取、特征匹配三个部分,其中预处理部分又可分为归一化、图像滤波增强、二值化和细化等几个步骤。系统流程框图如图1-1所示。下面对这三个个部分做一下简单的介绍。          指纹图像处理中物体的形状信息是十分重要的,为了提取指纹图像特定区域的特征,对指纹图像通常需要采用细化算法处理,得到与原来指纹图像形状近似的由简单的弧或曲线组成的图形,这些细线处于物体的中轴附近,这就是所谓的指纹图像的细化。       细化方法不同,细化结果就有差异。在指纹识别中要求在不改变原来指纹图像的拓朴连通性的同时,细化的结果应为严格的八邻域图像骨架;纹线中除去特征点以外,每个像素均只与相邻两个像素为八邻域,抹去任意一像素都将破坏纹线的连接性。概括起来说就是纹线细化处理要满足收敛性、连接性、拓朴性、保持性、细化性、中轴性、快速性的要求。目前为止,关于细化方法的研究工作已有很多成果,所采用的方法从使用的观点来看,比较多的是采用模板匹配的方法(如迭代法、OPTA单连通法等)。这种方法是根据某个像素的局部邻域(如3×3,5×5等)的图像特征对其进行处理,此外也有采用边缘搜索编码、外轮廓计算以及神经网络等细化方法。从处理的过程来看,主要可以分为串行和并行两类,前者对图像中当前像素处理依据其邻域内像素的即时化结果,且不同的细化阶段采用不同的处理方法;后者对当前的像素处理该像素及其邻域内各像素的前一轮   迭代处理的结果,自始至终采用相同的细化准则。         对于任意形状的区域,细化实质上是腐蚀操作的变体,细化过程中要根据每个像素点的八个相邻点的情况来判断该点是否可以剔除或保留。          细节特征提取的方法分为两种:一种是从灰度图像中提取特征,另一种是从细化二值图像中提取特征。直接从灰度图像中提取特征的算法一般是对灰度指纹纹线进行跟踪,根据跟踪结果寻找特征的位置和判断特征的类型。这种方法省去了复杂的指纹图像预处理过程,但是特征提取的算法却十分复杂,而且由于噪声等因素影响,特征信息(位置、方向等)也不够准确。目前大多数系统采用第二种方法,从细化二值图像中提取特征,该方法比较简单,在得到可靠的细化二值图像后,只需要一个3×3的模板就可以将端点和分叉点提取出来。          特征点提取的好坏将直接影响匹配的结果。现实中,指纹输入时,由于汗渍、干燥、按压力度不同等影响,得到的指纹图像大都含有断纹、褶皱、模糊、灰度不均匀等质量问题,虽然经过预处理,图像质量会有所改观,但预处理算法对各个指纹的适应性和有效性也会不同,并且会引入新的噪声,因此得到的细化二值图像往往含有大量的伪特征点。伪特征点不仅会影响匹配的速度,严重的会影响整个识别的正确率。所以提取特征点后要进行去伪处理,尽可能滤除伪特征点、保留真特征点。实践中发现,伪特征点的数量一般占总特征数量的一半以上,所以去伪是必不可少的过程。去伪过程可以在两个阶段进行:一是在特征提取之前对细化二值图像进行平滑、去除毛刺、连接断纹等操作,然后提取特征作为真特征;另一种是在特征提取之后,根据特征之间的相互关系,尽可能准确的识别伪特征点并滤除它们。前者直接对图像进行修补,操作比较复杂,容易引入新的伪特征;后者对特征提取后的数据进行判断,识别比较麻烦,但是速度较快本文采用第二种方法,即从已提取的特征点中滤除伪特征,保留真特征。  二、核心程序 clc; clear all; close all; warning off;   %读取图片 %注意,为了和论文中的结果对应起来,我们使用论文中的图片进行代码的测试 I=imread('images.jpg'); if isrgb(I) == 1    I = rgb2gray(I); else    I = I; end I_tmp = I;     figure; subplot(121); imshow(I);   %图像预处理,去噪滤波 I = medfilt2(I,[3,3]);%进行中值滤波; subplot(122); imshow(I);   %背景分割 %图像二值化处理 %首先进行归一化 I=norms(I,120,600);     figure; %为了显示处理效果,需要数据类型转换 subplot(121); imshow(double(I));   Ker   = 8; [m,n] = size(I); m1    = m/Ker; n1    = n/Ker; for i=1:m1    for j=1:n1        %设定门限       t=mean2(I((i-1)*Ker+1:(i-1)*Ker+Ker,(j-1)*Ker+1:(j-1)*Ker+Ker));       for k=(i-1)*Ker+1:(i-1)*Ker+Ker           for l=(j-1)*Ker+1:(j-1)*Ker+Ker               if I(k,l) < t                   I(k,l)=1;               else                    I(k,l)=0;               end           end       end    end end     %为了显示处理效果,需要数据类型转换 subplot(122); imshow(double(I));           %异常点的处理 for i=1:m     for j=1:n         if I(i,j)>1             I(i,j)=0;         end     end end for i=1:m     for j=1:n         if I(i,j)==1             for k=1:j                 I(i,k)=1;             end             break;         end     end end for i=1:m     for j=n:-1:1         if I(i,j)==1             for k=n:-1:j                I(i,k)=1;             end             break;    end     end end for i=1:m     for j=1:n         if I(i,j)==1             I(i,j)=0;         else             I(i,j)=1;         end     end end figure; %为了显示处理效果,需要数据类型转换 imshow(double(I));   %对细化前的图像进行维纳滤波 figure; subplot(121); imshow(double(I));   I=wiener2(I,[3 3]);   subplot(122); imshow(double(I));   %以下为细化处理 [r,c] = size(I); for i1=1:r;     for j1=1:c         if (I(i1,j1)==1)            I(i1,j1)= 255;             I2      = I;         end     end end     y = thinning(I2); figure; imshow(y);     %以上的代码是你之前的第三章的代码的仿真图 %以下的代码是对应第四章的仿真图 %首先需要提取指纹图像的中心点——对应论文的“图4-1提取中心点后的细化图像” [XC,YC] = find_central_point(y);   %找寻细化图像的特征点 %特征点的建立——对应论文的“图4-2特征点提取后的图像” [dpx3,dpy3,dpcount3,fpx3,fpy3,fpcount3]=characterpoint(y); figure; imshow(y); hold on; plot(dpy3,dpx3,'o'); hold on; plot(fpy3,fpx3,'+'); hold on; plot(XC,YC,'*r');  hold off;           %以下代码是对最后的识别结果和数据库中的进行匹配 %计算每个特征点到中心点的距离, Dpcount=size(dpx3,2); Fpcount=size(fpx3,2); for i=1:Dpcount     DP_Feature(i)=sqrt((dpx3(i)-YC)^2+(dpy3(i)-XC)^2); end for j=1:Fpcount     FP_Feature(j)=sqrt((fpx3(j)-YC)^2+(fpy3(j)-XC)^2); end      %计算指纹图像的方向角 theta = func_theta(I_tmp);   %建立待识别的特征库 for i=1:Dpcount     PointOfModel(i,1)=1;     PointOfModel(i,2)=DP_Feature(i);     PointOfModel(i,3)=theta(dpx3(i),dpy3(i))-theta(YC,XC); end for i=Dpcount+1:Dpcount+Fpcount     PointOfModel(i,1)=2;     PointOfModel(i,2)=FP_Feature(i-Dpcount);     PointOfModel(i,3)=theta(fpx3(i-Dpcount),fpy3(i-Dpcount))-theta(YC,XC); end         %调用database_feature计算得到PointOfMatch I=imread('database/22.tif');   if isrgb(I) == 1    I = rgb2gray(I); else    I = I; end PointOfMatch = database_feature(I);   %调用MATCH函数进行匹配 %图4-3 匹配后的图像 NUM = 90;%这个参数是新加的,设置相似度,即多少个匹配点对上才算成功 Count=match(y,PointOfModel,PointOfMatch,dpx3,dpy3,fpx3,fpy3,NUM);        三、测试结果 边缘保护  请查看原文图片  进行适应性去噪过滤处理  请查看原文图片  细化处理得到的结果  请查看原文图片 二值化图像细化处理,并标注特征点和中心点。特征端点用'o'标注,特征分叉点用'+'标注,中心点用红色'*'标注  请查看原文图片 指纹匹配  ​​​​​​​请查看原文图片​​​​​​​A10-11 ———————————————— 版权声明:本文为CSDN博主「fpga和matlab」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ccsss22/article/details/127582230 
  • [问题求助] 如何使用java开发对openguass数据库的应用呢?
    如题,网上使用jdbc,odbc连接openguass的教程太少了,求推荐!
  • [技术干货] 这些Java基础知识,诸佬们都还记得嘛(学习,复习,面试都可)-转载
     双亲委派模型工作流程 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当上一层类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到这个类)时,下一层类加载器才会尝试自己去加载。  双亲委派模型的好处: 1.可以确保我们的程序安全。比如,我们自定义一个包名为java.lang的包,类名为String的类,此时会报出"java.lang.String中找不到类方法"的错误,这是因为在父类加载器中以及存在了String类,而父类加载器中的String类没有main方法,因此报错。这也是我们初学java时经常犯的一个小错误。  那么如何打破JVM双亲委派模型呢?  想要打破JVM双亲委派模型,需要我们自己自定义一个类加载器,然后重写其中的loadClass方法,使其不进行双亲委派流程就行了。  🍖StringTable具有哪些特性 首先,在JDK1.8的版本中,StringTable是位于JVM内存结构的堆中的。而堆中经常存储一些所以创建的对象,数组等。  那么StringTable具有哪些特性呢?  常量池中的字符串仅是符号,第一次用到时才变为对象 利用串池的机制,来避免重复创建字符串对象 字符串变量拼接的原理是StringBuilder(1.8) 字符串常量拼接的原理是编译期优化 可以使用intern方法,主动将串池中还没有的字符串对象放入串池 在jdk1.8版本中会将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象返回 在jdk1.6版本中会将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池,会把串池中的对象返回   🍹常量池面试题 常量池就是一张表,虚拟机指令根据这张表找到要执行的类名,方法名,参数类型,字面量等信息。 分享一道经典的关于常量池的面试题,后面有每一步的详细注释  public static void main(String[] args){ String s1 ="a";//加载到这一步会将"a"放入常量池中 String s2= "b";//加载到这一步会将"b"放入常量池中 String s3 ="a"+"b";// 在编译期间会进行优化对字面量运算得出结果,池中并没有"ab",因此将"ab"放入池中 String s4 = s1 + s2; // 这一步并不会直接将s1和s2相加,而是先加载s1和s2的值,此时拼接的原理是利用StringBuilder,append()方法,newString("ab"),将此对象放入堆中 String s5 ="ab";//从字符串常量池中取对象,因此s5和s3是一样的 String s6 = s4.intern();//因为串池中有了字符串"ab",所以s6是和s3,s5一样的 System.out.println(s3==s4);// 结果为false,s3是串池中的,s4是堆中的 System.out.println(s3== s5); // 结果为true System.out.println(s3 == s6); // 结果为true  String x2 = new String("c")+ new String("d"); // 串池中没有c和d,因此会在堆中创建对象,同时会把"c"和"d"丢到串池中,x2也是通过StringBuilder的append()方法拼接而成 ,new String("cd")  x2.intern(); //常量池中没有"cd"对象,会将x2入池,同时x2也存在于堆中 String x1="cd";//直接从串池中取出"cd" System.out.print1n(x1 ==x2);// 结果为true  🥂什么情况下对象会进入老年代 🍵根据垃圾回收次数 默认情况下,经历了15次垃圾回收,仍然没有被垃圾回收器回收的将会进入老年代,当然也可以通过指令设置次数:通过JVM参数“-XX:MaxTenuringThreshold”来设置次数;  ✨根据动态对象年龄判断 动态对象年龄判断:虚拟机并不是要求对象的年龄必须达到了MaxTenuringThreshold=15才能晋升老年代;其实是Survivor区的对象年龄会从小到大进行累加,当累加到X年龄(某个年龄)时占用空间的总和大于50%(可以使用-XX:TargetSurvivorRatio=?来设置保留多少空闲空间,默认值是50),那么比X年龄大的对象都会晋升到老年代;   🚙老年代空间担保机制 3.老年代空间担保机制: (1)执行任何一次MinorGC之前,JVM会先检查一下老年代可用内存空间,是否大于新生代所有对象的总大小,因为在极端情况下,可能新生代 Minor GC之后,新生代所有对象都需要存活,那就会造成新生代所有对象全部要进入老年代; (2)如果老年代的可用内存大于新生代所有对象总大小,此时就可以放心大胆的对新生代发起一次Minor GC,因为 Minor GC之后即使所有对象都存活,Survivor区放不下了,也可以转移到老年代去; (3)如果执行 Minor GC之前,检测发现老年代的可用空间已经小于新生代的全部对象总大小,那么就会进行下一个判断,判断老年代的可用空间大小,是否大于之前每一次Minor GC后进入老年代的对象的平均大小,如果判断发现老年代的内存大小,大于之前每一次Minor GC后进入老年代的对象的平均大小,那么就是说可以冒险尝试一下Minor GC,但是此时真的可能有风险,那就是Minor GC过后,剩余的存活对象的大小,大于Survivor空间的大小,也大于老年代可用空间的大小,老年代都放不下这些存活对象了,此时就会触发一次“Full GC”;所以老年代空间分配担保机制的目的?也是为了避免频繁进行Full GC; (4)如果 Full GC之后,老年代还是没有足够的空间存放 Minor GC过后的剩余存活对象,那么此时就会导致“OOM”内存溢出。  🍬大对象会直接进入老年代 大对象是指需要大量连续内存空间的Java对象,比如很长的字符串或者是很大的数组或者List集合,大对象在分配空间时,容易导致内存明明还有不少空间时就提前触发垃圾回收以获得足够的连续空间来存放它们,而当复制对象时,大对象又会引起高额的内存复制开销,为了避免新生代里出现那些大对象,然后屡次躲过GC而进行来回复制,此时JVM就直接把该大对象放入老年代,而不会经过新生代; 我们可以通过JVM参数“-XX:PretenureSizeThreshold”设置多大的对象直接进入老年代,该值为字节数,比如“1048576”字节就是1MB,该参数表示如果创建一个大于这个大小的对象,比如一个超大的数组或者List 集合,此时就直接把该大对象放入老年代,而不会经过新生代;-XX:PretenureSizeThreshold参数只对Serial和ParNew两款新生代收集器有效,其他新生代垃圾收集器不支持该参数,如果必须使用此参数进行调优,可考虑ParNew+CMS的收集器组合。   🍇Java中不同的引用类型 Java里有不同的引用类型,分别是强引用、软引用、弱引用和虚引用。  强引用:所有的GC Roots对象都不通过强引用引用该对象,该对象才能被垃圾回收,Object object =new Object(); 软引用:SoftReference 内存充足时不回收,内存不足时则回收,可以配合引用队列来释放引用自身; 弱引用:WeakReference不管内存是否充足,只要GC一运行就会回收该引用对象,可以配合引用队列来释放弱引用自身; 虚引用:PhantomReference这个其实暂时忽略也行,因为很少用,它形同虚设,就像没有引用一样,其作用就是该引用对象被GC回收时候触发一个系统通知,或者触发进一步的处理;  🍓零钱兑换算法题 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。  计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。  你可以认为每种硬币的数量是无限的。  示例 1:  输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1  示例 2:  输入:coins = [2], amount = 3 输出:-1  示例 3:  输入:coins = [1], amount = 0 输出:0  class Solution {     public int coinChange(int[] coins, int amount) {         int max=amount+1;         int [] dp=new int [max];//定义数组长度,从0开始需要+1         Arrays.fill(dp,max);//赋初值,让求的是最小值,将每一步用最大值填充         dp[0]=0;//下标为0的数值为0,因为下标为0没有数         for(int i=1;i<=amount;i++){             for(int j=0;j<coins.length;j++){                 if(coins[j]<=i){//根据coin值与当前遍历i的大小判断,否则dp[负数]不合题意                     dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);//转移方程,没多遍历一次,就会更新dp[i]的值                 }             }         }         return dp[amount]>amount ? -1 :dp[amount];//如果初始给定的值大于最终值,则返回-1,表示不成立     } } ———————————————— 版权声明:本文为CSDN博主「小威要向诸佬学习呀」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_53847859/article/details/127043595 
总条数:2294 到第 页
上滑加载中