菜单

java基础和踩坑汇总

duckflew
发布于 2020-08-02 / 314 阅读
0
0

java基础和踩坑汇总

java文件分类 以分类图片为例

这个案例里面遇到一个小问题

在file.delete之前必须要先把FileInputStream关闭掉才可以删除成功
另外在 保存文件的时候要判断一下 你需要分类的目录下有没有同名文件 有的话让文件名字++跳过这个文件

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.*;
public class GetPx
{
    public static void main(String[] args) throws IOException {
        int twoKIndex=0;
        int phoneIndex=0;
        int commonIndex=0;
        String dirPath="F:\\图片\\壁纸\\鬼刀\\wlop鬼刀合集";
        File dir=new File(dirPath);
        File[] files=dir.listFiles();
        for(File file:files)
        {
            if(file.isFile()&&file.getName().contains(".jpg")) {
                BufferedImage bi = null;
                try {
                    bi = ImageIO.read(file);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                int width = bi.getWidth(); // 像素
                int height = bi.getHeight(); // 像素
                FileInputStream fis=new FileInputStream(file);
                if(width<height)
                {
                    System.out.println(file.getName() + ":width=" + width + ",height=" + height+"分类为手机壁纸");
                    File wantToSaveFile=new File(dirPath+"\\手机\\"+(++phoneIndex)+".jpg");
                    while(wantToSaveFile.exists())wantToSaveFile=new File(dirPath+"\\手机\\"+(++phoneIndex)+".jpg");
                    FileUtils.writeByteArrayToFile(wantToSaveFile,IOUtils.toByteArray(fis));
                    fis.close();
                    boolean delete = file.delete();
                    if (delete) System.out.println("删除成功");
                    else System.out.println("删除失败");

                }
                else if (width > 1800) {
                    System.out.println(file.getName() + ":width=" + width + ",height=" + height+"分类为2k壁纸");
                    File wantToSaveFile=new File(dirPath+"\\2k\\"+(twoKIndex++)+".jpg");
                    while(wantToSaveFile.exists())wantToSaveFile=new File(dirPath+"\\2k\\"+(++twoKIndex)+".jpg");
                    FileUtils.writeByteArrayToFile(wantToSaveFile,IOUtils.toByteArray(fis));
                    fis.close();
                    boolean delete = file.delete();
                    if (delete) System.out.println("删除成功");
                    else System.out.println("删除失败");

                }
                else
                {
                    System.out.println(file.getName() + ":width=" + width + ",height=" + height+"分类为普通壁纸");
                    /**
                     * 判断文件是否存在
                     */
                    File wantToSaveFile=new File(dirPath+"\\普通\\"+(commonIndex++)+".jpg");
                    while(wantToSaveFile.exists())wantToSaveFile=new File(dirPath+"\\普通\\"+(++commonIndex)+".jpg");
                    FileUtils.writeByteArrayToFile(wantToSaveFile,IOUtils.toByteArray(fis));
                    fis.close();
                    boolean delete = file.delete();
                    if (delete) System.out.println("删除成功");
                    else System.out.println("删除失败");
                }
            }
        }
        System.out.println("文件分类完成 分类结果如下");
        System.out.println("普通壁纸:"+commonIndex+"张");
        System.out.println("2k壁纸:"+twoKIndex+"张");
        System.out.println("手机壁纸:"+phoneIndex+"张");
    }
}


sizeof不是java中的关键字 const和goto都属于保留字也算是关键字

java数据溢出的话会循环 比如说如果int的范围是-3到2 那么如果-3再-1的话得到就是2 例题如下

int i=Integer.MIN_VALUE;
System.out.println((i-1)+(i-1L));

输出-2 i-1得到的就是Integer.MAX_VALUE i-1L的话因为范围变大了 所以的数据变成了Integer.MIN_VALUE-1; 而数据的范围就是maxvlalue要比minvalue的绝对值小1的 表达式的值就是-2

为了实现跨平台 文件读取的时候文件的路径的分隔符可以用

File.separator来实现它返回一个字符串代表分隔符

老错误了 形参没办法直接修改原来的实参的指向

public static void main(String[] args)
    {
        String abc="abc";
        add(abc);
        System.out.println(abc);
    }
    public static void add(String abc)
    {
        abc+="xyz";
    }

还是输出abc

final的几个基本定义

  • final修饰变量,则等同于常量
  • final修饰方法中的参数,称为最终参数。
  • final修饰类,则类不能被继承
  • final修饰方法,则方法不能被重写。
  • final 不能修饰抽象类
  • final修饰的方法可以被重载 但不能被重写
  • final类中的所有成员方法都会被隐式地指定为final方法不需要自己写

catch如果有多个匹配的异常 那就执行第一个catch 例如第一个catch捕捉的是Exception e 那就下面的catch永远不会执行了

几个集合框架的小点

  • ArrayList的底层是数组
  • LinkedList底层是链表
  • Map那一家子的底层是哈希表
  • TreeSet的底层是红黑树
  • HashSet的底层是HashMap

内部类可以声明为private protected 也可以没有名字

Thread.start之后 线程处于就绪状态等待CPU调用

statement执行sql语句没有excuteSelect()方法 只有 excuteQuery返回ResultSet excute()返回boolean表示是否执行成功 excuteUpdate()返回int表示更改的行数

面向对象的三大特征 封装继承多态

受控异常为必须处理的异常

受控异常:Checked Exception,这类异常必须写try{}catch{},或者throw抛出,否则编译通不过。
非受控异常:Unchecked Exception,这类异常也叫做运行时异常(与非受控异常 字数相等),这类异常不需要try{}catch{},也不需要throw抛出,编译能通过。为什么要使用非受控异常?为了简化代码。试想一下,如果所有可能出现异常的地方(比如访问数组元素可能会越界、调用对象方法,对象可能为null),我们都写try{}catch{},或者throw 抛出,那么代码肯定冗余的不成样子了。也就是说,采用非受控异常(运行时异常)可以减少代码的污染。
对于非受控异常(运行时异常),因为不需要额外处理,也能编译通过,我们可以进行预先检查,比如访问数组元素时,我们预先检查是否越界,调用对象方法时,预先检查对象是否为null

Comparable为集合内部实现的排序 Comoarator为集合外实现的排序

运行一个java程序时 类被使用到 才加载 定义了几个类就会生成多少个类文件 就算是内部类也算

标签和文本框都可以直接添加到JFrame或者JPanel里面去因为他们都是顶层容器

类的构造方法可以被定义为private类型的 列入Enum枚举类型

子类必须显式 调用父类的构造方法 如果父类有默认构造 那子类你不写super.构造 也是可以的相当于编译器默认帮你调用了 但是如果父类没有默认构造的情况下那就必须要再子类的构造里面调用父类的构造方法

JDBC基本操作

String url="jdbc:mysql://localhost:3306/bigBlog?serverTimezone=GMT";
String username="root";
String password="yourPassword";
Class.forName("com.mysql.cj.jdbc.Driver");
try
{
    Connection connection=DriverManager.getConnection(url,username,password);
    String sql="select * from blog_user where id=?";
    PreparedStatement preparedStatement=connection.prepareStatement(sql);
    preparedStatement.setInt(1,1);
    ResultSet resultSet = preparedStatement.executeQuery();
    ResultSetMetaData metaData = resultSet.getMetaData();
    int columnCount = metaData.getColumnCount();
    for (int i = 1; i <= columnCount; i++)
    {
        System.out.print(metaData.getColumnName(i));
        System.out.print("  ");
    }
    System.out.println();
    resultSet.next();
    for (int i = 1; i <=columnCount ; i++)
    {
        System.out.print(resultSet.getString(i));
        System.out.print("  ");
    }
} 
catch (SQLException throwables)
{
    throwables.printStackTrace();
}

java标识符必须以$,字母,_开头 后面可以接这三个和数字

可以给java的循环添加标记 然后break这个标记 但是标记必须放在循环的上一句 否则会编译失败

 z:
  for (int i = 0; i < 10; i++)
  {
     for (int j = 0; j < 11; j++)
     {
      System.out.println(j);
       if (j==3)break z;
     }
  }

变量隐藏

java中子类如果与父类有同名的变量 则父类的变量会被隐藏 但是仍然可以用super来访问 但是方法只会被覆盖
观察如下代码

package cn.duckflew.JavaClubTest;
import lombok.ToString;

@ToString
class A
{
    public String s="A";

    public String getS()
    {
        return s;
    }

    public void setS(String s)
    {
        this.s = s;
    }
}
@ToString
public class B extends A
{
    public String s="B";

    @Override
    public String getS()
    {
        return s;
    }

    @Override
    public void setS(String s)
    {
        this.s = s;
    }

    public static void main(String[] args)
    {
        A a = new A();
        B b = new B();
        System.out.println(a.s);
        System.out.println(b.s);
        a.setS("[AA]");
        b.setS("[BB]");
        a=b;
//准确来说一直到这一句  b对象中都含有两份S 一个是"A" 一个是"BB"
        System.out.println(a.s);
        System.out.println(b.s);
        System.out.println(a.getS());
        System.out.println(b.getS());
    }
}

输出如下

A
B
A
[BB]
[BB]
[BB]

多线程之生产者和消费者案例

这个案例就是构造 了一个box对象作为共享数据区 在box里面添加同步代码块实现线程安全
奶箱类:Box 定义一个成员变量表示第几瓶奶 提供存储牛奶和取出牛奶的操作
生产者类:Producer 实现Runnable接口 重写run 调用存储牛奶的操作
消费者类 Consumer 实现Runnable接口 重写run 调用取出牛奶的操作
测试类 BoxDemo 里面的main方法main方法的步骤如下

1 创建奶箱对象 这是共享数据的区域
2 创建生产者对象 把奶香对象作为构造方法传入进去 因为在这个类要实现存储牛奶的操作
3 创建消费者对象 把奶箱作为构造方法传入进去  因为在这个类中要实现取出牛奶的操作
4 创建两个线程对象  分别传入消费者和生产者作为2个线程
5 启动线程

代码如下

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Producer implements Runnable
{
    private Box box;
    @Override
    public void run()
    {
        for (int i = 1; i <= 35; i++)
        {
            box.put(i);
        }
    }
}
-------------------------------------------------
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Consumer implements Runnable
{
    private Box box;
    @Override
    public void run()
    {
        while (true)
        {
            box.get();
        }
    }
}
---------------------------------------------------
public class Box
{
    private int milk;
    private boolean statue;
    public synchronized void put(int milk)
    {
        if (statue)
        {
            try
            {
                wait();
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
        this.milk = milk;
        System.out.println("送奶工把第" + milk + "放入奶箱");
        statue=true;
        notifyAll();
    }
    public synchronized void get()
    {
        if (!statue)
        {
            try
            {
                wait();
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
        /**
         * 如果有牛奶  就消费
         */
        System.out.println("用户拿到第"+milk+"瓶奶");
        statue=false;
        notifyAll();
    }
}

BoxDemo测试代码

 	Box box = new Box();
        Producer producer = new Producer(box);
        Consumer consumer = new Consumer(box);
        Thread thread=new Thread(producer);
        Thread thread1 = new Thread(consumer);
        thread.start();
        thread1.start();
送奶工把第1放入奶箱
用户拿到第1瓶奶
送奶工把第2放入奶箱
用户拿到第2瓶奶
送奶工把第3放入奶箱
用户拿到第3瓶奶
..........................以此类推

java数据类型的大小 以及数据类型的接收

  • char 2字节
  • int 4字节
  • double 8字节
  • short 2字节
  • long 8字节

重要的一点:小可转大,大转小会失去精度!!
低数据类型可以直接赋值给高数据类型,反之,高数据类型转换为低数据类型必须强转,即提前制定数据类型,例 int a = (int) 0.0F 并不会像C语言一样自动截断
大转大 要看是是否属于同一大类 比如long就不能转化double 反过来也不行 他们都是8byte

java访问控制修饰符

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。

  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

  • public : 对所有类可见。使用对象:类、接口、变量、方法

  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。

基本递归问题--兔子繁殖

有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问几个月后的兔子总数为多少?

第三个月可以生兔子 说明兔子需要两个月的成长时间
可以得到兔子数量满足斐波那契数列
1 1 2 3 5 8 13 21......
如果成长时间是三个月呢?
通过java模拟这个过程可以发现 也是有规律的 代码在最底下
1,1,1,2,3,4,6,9,13,19,28,41,60,88,129,189,277,406,595,872,1278,1873,2745,4023
这里其实可以看做是一个跳跃式的斐波那契数列 下标从1开始
A4=A1+A3 A5=A2+A4 A6=A3+A5.....
如此
由此可以推倒出一般的递归表达式
假设成长时间为m 则从A1-Am都是1
从x>m开始
A(x)=A(x-1)+A(x-m)

package cn.duckflew;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Setter
@Getter
class Rabbit
{
    private int age;

    public Rabbit()
    {
        age=0;
    }
}
public class RabbitCal
{
    public static void main(String[] args)
    {
        for (int month=1;month<=24;month++)
        {
            Rabbit root = new Rabbit();
            List<Rabbit> rabbits=new ArrayList<>();
            rabbits.add(root);
            for (int i = 1; i <= month; i++)
            {
                for (int j = 0; j < rabbits.size(); j++)
                {
                    Rabbit rabbit = rabbits.get(j);
                    int age = rabbit.getAge();
                    if (age >= 3)
                    {
                        rabbits.add(new Rabbit());
                    }
                }
                for (int j = 0; j < rabbits.size(); j++)
                {
                    Rabbit rabbit = rabbits.get(j);
                    int age = rabbit.getAge();
                    rabbit.setAge(age + 1);
                }
            }
            System.out.println(rabbits.size());
        }
    }
}

java Final修饰的变量称为被视为常量(true)

sleep 不释放对象锁 wait 放弃对象锁

java线程同步以及锁

  1. 对象锁
    在 Java 中,每个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常会被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。
  2. 类锁
    在 Java 中,针对每个类也有一个锁,可以称为“类锁”,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁。

1.使用synchronized(object) 可以作为一把锁 被同一把锁锁住的代码块是线程安全的

  • 修饰代码块时 synchronized(object) 使用的object 是同一个对象就行
  • 修饰方法时 synchromized 没有括号 其实里面默认放的就是this因为this对象属于同一个对象
    public synchronized void sellOneTicket()
    

    其他的代码块需要加 synchronized(this)

  • 修饰静态方法 synchromized 默认的锁对象加载的是类的class对象的对象锁
    public static synchronized void ssss()
    

    其他的代码块需要加 synchronized(类名.class)

最大公约数和最小公倍数(辗转相除法)

 /**
     * 非递归
     * @param m
     * @param n
     * @return
     */
    public static int gcd(int m,int n)
    {
        if (n==0) throw new IllegalArgumentException("除数不能为0!");
        if (m<n)
        {
            int temp=m;
            m=n;
            n=temp;
        }
        int r;
        while (m%n!=0)
        {
            r=m%n;
            m=n;
            n=r;
        }
        return n;
    }
 /**
     * 递归
     * @param m
     * @param n
     * @return
     */
    public static int gcdDigui(int m,int n)
    {
        if (n==0) throw new IllegalArgumentException("除数不能为0!");
        if (m%n==0)return n;
        return gcd(n,m%n);
    }
public static int lcm(int m,int n)
    {
        if (n==0) throw new IllegalArgumentException("除数不能为0!");
        return m*n%gcd(m,n);
    }

抽象类的概念

  • 抽象方法不能有实现体 只能通过子类实现
  • 抽象类无法被实例化 除非用父类指向子类的构造
  • 接口相当于极度抽象的抽象类 我们一般不在接口中定义成员变量
  • 当父类没有默认构造的情况下抽象类的子类必须复写抽象父类的构造方法

java数组排序(自定义排序规则)

方法一

Arrays.sort(arr,Collections.reverseOrder());
//这种方法只适用于arr是对象数组的情况

方法二

 Integer [] arr={1,2,3,6,4,4};
        Arrays.sort(arr);
        System.out.println("顺序:");
        System.out.println(Arrays.toString(arr));
        System.out.println("逆序");
        //Arrays.sort(arr, (o1, o2) -> o2-o1);  下面这种写法与这一句等效
        Arrays.sort(arr, new Comparator<Integer>()
        {
            @Override
            public int compare(Integer o1, Integer o2)
            {
                return o2-o1;
            }
        });
        System.out.println(Arrays.toString(arr));

另外的 对于Int[] 好像并没有直接的方法能够对其进行逆序排序 其实要使用的时候也很简单 直接从尾部遍历 或者是排序完成新建个数组倒序转存一下
或者转换成Integer[] 或者存储到一ArrayList

对象排序

@Data
public class Student
{
    private int score;
    private String stuNo;
    private String name;
}

比较代码(成绩降序,学号升序,姓名字典序升序 这三项优先级递减)

Student heMeiLun = new Student();
        heMeiLun.setScore(99);
        heMeiLun.setName("heMeiLun");
        heMeiLun.setStuNo("201913137156");


        Student liLiang = new Student();
        liLiang.setScore(99);
        liLiang.setName("liLiang");
        liLiang.setStuNo("201913137156");


        Student huangRongHua= new Student();
        huangRongHua.setScore(64);
        huangRongHua.setName("huangRongHua");
        huangRongHua.setStuNo("201913137159");

        Student[] students={liLiang,heMeiLun,huangRongHua};
        Arrays.sort(students, new Comparator<Student>()
        {
            @Override
            public int compare(Student o1, Student o2)
            {
                if (o1.getScore()!=o2.getScore())
                {
                    return o2.getScore()-o1.getScore();
                }
                else
                {
                    if (o1.getStuNo()!=o2.getStuNo())return o1.getStuNo().compareTo(o2.getStuNo());
                    else return o1.getName().compareTo(o2.getName());
                }
            }
        });

lambda的写法

Arrays.sort(students, (o1, o2) ->
        {
            if (o1.getScore()!=o2.getScore())
            {
                return o2.getScore()-o1.getScore();
            }
            else
            {
                if (o1.getStuNo()!=o2.getStuNo())return o1.getStuNo().compareTo(o2.getStuNo());
                else return o1.getName().compareTo(o2.getName());
            }
        });

Map的遍历方法

  • 方法一 在for-each循环中使用entries来遍历

     Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
    
            System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
    
    
    

    注意:for-each循环在java 5中被引入所以该方法只能应用于java 5或更高的版本中。如果你遍历的是一个空的map对象,for-each循环将抛出NullPointerException,因此在遍历前你总是应该检查空引用。

  • 方法二 在for-each循环中遍历keys或values。

    如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。

    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    //遍历map中的键
    for (Integer key : map.keySet()) {
        System.out.println("Key = " + key);
    }
    //遍历map中的值
    for (Integer value : map.values()) {
        System.out.println("Value = " + value);
    }
    
    

    该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。

  • 方法三 使用Iterator遍历

    使用泛型:

    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
    while (entries.hasNext()) {
        Map.Entry<Integer, Integer> entry = entries.next();
        System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
    

    不使用泛型:

    Map map = new HashMap();
    Iterator entries = map.entrySet().iterator();
    while (entries.hasNext()) 
    {
        Map.Entry entry = (Map.Entry) entries.next();
        Integer key = (Integer)entry.getKey();
        Integer value = (Integer)entry.getValue();
        System.out.println("Key = " + key + ", Value = " + value);
    }
    

    你也可以在keySet和values上应用同样的方法。

    该种方式看起来冗余却有其优点所在。首先,在老版本java中这是惟一遍历map的方式。另一个好处是,你可以在遍历时调用iterator.remove()来删除entries,另两个方法则不能。根据javadoc的说明,如果在for-each遍历中尝试使用此方法,结果是不可预测的。

    从性能方面看,该方法类同于for-each遍历(即方法二)的性能

如果仅需要键(keys)或值(values)使用方法二。如果你使用的语言版本低于java 5,或是打算在遍历时删除entries,必须使用方法三。否则使用方法一(键值都要)。

递归和非递归方式遍历文件夹

递归
    /**
     * 遍历文件()
     * @param rootFile 文件夹
     * @return
     */
    public static List<File> findAllFiles(File rootFile){
        List<File> results = new ArrayList<>();
        if(rootFile.exists()){
            if(rootFile.isDirectory()){
                for(File file : rootFile.listFiles()) {
                    results.addAll(findAllFiles(file));
                }
            }else{
                results.add(rootFile);
            }
        }
        return results;
    }
 
    public static void main(String[] args){
        //测试
        for(File file : findAllFiles(new File("/Users/xxxx/Desktop/"))){
            System.out.println(file.getAbsolutePath());
        }
    }
非递归
    /**
     * 遍历文件目录(非递归)
     *
     * @param rootFile 文件夹
     * @return
     */
    public static List<File> findFiles(File rootFile) {
        if (!rootFile.exists())
            return new ArrayList<>();
        //结果集
        List<File> results = new ArrayList<>();
        //存储文件夹
        List<File> files = new ArrayList<>();
 
        if (rootFile.isDirectory()) {
            for (File file : rootFile.listFiles()) {
                if(file.isDirectory()){
                    files.add(file);
                }else{
                    results.add(file);
                }
            }
        } else {
            results.add(rootFile);
        }
        while (!files.isEmpty()){
            File fileDir = files.remove(0);
            for (File zfile : fileDir.listFiles()) {
                if(zfile.isDirectory()){
                    files.add(zfile);
                }else{
                    results.add(zfile);
                }
            }
        }
        return results;
    }
 
    public static void main(String[] args) {
        String path = "/Users/xxxx/Desktop/";
        //测试
        List<File> list2 = findFiles(new File(path));
        System.out.println(list2.size());
        for (File file : list2){
            System.out.println(file.getAbsolutePath());
        }
    }

评论