java基础知识和JDBC

StringBuffer与String的相互转换

1将StringBuffer转换成String

  StringBuffer类成员toString函数可将其转换成String类型。实例如下:

StringBuffer stringBuffer = new StringBuffer(“Hello World.”);

  String c = stringBuffer.toString();// 调用toString函数将StringBuffer对象转换成String对象

2将String转换成StringBuffer

    方式一:利用构造函数

   String str=Hello World.;

   StringBuffer buffer = new StringBuffer(str);

    方式二:调用append函数

   String str=Hello World.;       

   StringBuffer buffer2 = new StringBuffer();

   buffer2.append(str);

String与字符数组的相互转换

1将String转换成字符数组

  String类成员toCharArray函数可将其转换成字符数组。实例如下:

  String c =Hello World.;// 创建一个String对象

  char[] cd = c.toCharArray();// 再调用String对象的toCharArray函数转换成字

2将字符数组转换成String

  Java提供两种方法直接将字符数组转为字符串。

  方法1:利用String类的构造函数,直接在构造String时完成转换。

  char[] data = {'a', 'b', 'c'};

  String str = new String(data);

  方法2:调用String类的valueOf函数转换。

  String.valueOf(char[] ch);

将StringBuffer与字符数组的相互转换

将StringBuffer转换成字符数组

Java中不支持直接从StringBuffer转换成字符数组。而是先将StringBuffer转换成String,然后由String调用toCharArray函数转换成字符数组。转换实例如下:

   StringBuffer stringBuffer = new StringBuffer(Hello World.);

   String c = stringBuffer.toString();// 先将StringBuffer对象转换成String对象

   char[] cd = c.toCharArray();// 再调用String对象的toCharArray函数转换成字符数组

将字符数组转换成StringBuffer

  与将StringBuffer转换成字符数组类似,需要先将字符数组转换成String,然后再由String转换成StringBufferchar[] data = {'H', 'e', 'l','l', 'o', 'd'};

  String str = new String();

  str.valueOf(char[] ch);//调用String类的valueOf函数将字符数组转换成String  

  StringBuffer buffer = new StringBuffer(); 

  buffer.append(str);//调用append函数将String转换成Stringbuffer

集合和数组相互转换

很多时候API的方法只支持集合或数组,比如适配器类中的数据只支持集合,这时候我们就要把数组转换集合。

集合转换成数组

可以把String换成其他类

List<String>list;
String[] characters = list.toArray(new String[list.size()]);

数组转换成集合

String[] arr ;
List<String>list1= Arrays.asList(arr);

上面是使用API来完成,使用循环遍历也是可以方便做到的。
##数组转换成集合(循环遍历)

public static List<String> getList(String[] arr){
    List<String>list=new ArrayList<String>();
    for(int i=0;i<arr.length;i++){
        list.add(arr[i]);
    }
       return list;
}

将集合转换成数组(循环遍历)

  public static String[] getArr(List<String> list){
          String[] arr=new String[list.size()];
        for(int i=0;i<list.size();i++){
            arr[i]=list.get(i);
        }
        return arr;
    }

将字符串反转输出的一个示例

//被操作的字符串
String str = “Welcome today is a good day!”;

使用集合和数组的相互转换

//字符串转为字符数组  
char[] array = str.toCharArray();  
//数组转为集合  
List<Character> list = new ArrayList<Character>();  
for (int i = 0; i < array.length; i++) {  
    list.add(array[i]);  
}  
//反转  
Collections.reverse(list);  
//集合转为数组  
Character[] characters = list.toArray(new Character[list.size()]);  
//构建反转后的字符串  
StringBuilder sb = new StringBuilder();  
for (Character character : list) {  
    sb.append(character);  
}  
System.out.println(sb.toString());  

更简单的实现:使用StringBuilder的反转

StringBuilder sb = new StringBuilder(str);  
sb.reverse();  
System.out.println(sb.toString()); 

算法,从两头往中间挤着交换前后的位置

//字符串转为字符数组  
    char[] array = str.toCharArray();  
    for (int low = 0, high = array.length-1; low < high; low++, high--) {  
        //交换位置  
        char temp = array[low];  
        array[low] = array[high];  
        array[high] = temp;  
    }  

JAVA字符串格式化-String.format()的使用

常规类型的格式化

String类的format()方法用于创建格式化的字符串以及连接多个字符串对象。熟悉C语言的同学应该记得C语言的sprintf()方法,两者有类似之处。format()方法有两种重载形式。
format(String format, Object… args) 新字符串使用本地语言环境,制定字符串格式和参数生成格式化的新字符串。

format(Locale locale, String format, Object… args) 使用指定的语言环境,制定字符串格式和参数生成格式化的字符串。

显示不同转换符实现不同数据类型到字符串的转换,如图所示。

在这里插入图片描述

测试用例

public static void main(String[] args) {
    String str=null;
    str=String.format("Hi,%s", "王力");
    System.out.println(str);
    str=String.format("Hi,%s:%s.%s", "王南","王力","王张");          
    System.out.println(str);                         
    System.out.printf("字母a的大写是:%c %n", 'A');
    System.out.printf("3>7的结果是:%b %n", 3>7);
    System.out.printf("100的一半是:%d %n", 100/2);
    System.out.printf("100的16进制数是:%x %n", 100);
    System.out.printf("100的8进制数是:%o %n", 100);
    System.out.printf("50元的书打8.5折扣是:%f 元%n", 50*0.85);
    System.out.printf("上面价格的16进制数是:%a %n", 50*0.85);
    System.out.printf("上面价格的指数表示:%e %n", 50*0.85);
    System.out.printf("上面价格的指数和浮点数结果的长度较短的是:%g %n", 50*0.85);
    System.out.printf("上面的折扣是%d%% %n", 85);
    System.out.printf("字母A的散列码是:%h %n", 'A');
}

输出结果

Hi,王力
Hi,王南:王力.王张
字母a的大写是:A 
3>7的结果是:false 
100的一半是:50 
10016进制数是:64 
1008进制数是:144 
50元的书打8.5折扣是:42.500000 元
上面价格的16进制数是:0x1.54p5 
上面价格的指数表示:4.250000e+01 
上面价格的指数和浮点数结果的长度较短的是:42.5000 
上面的折扣是85% 
字母A的散列码是:41 

搭配转换符的标志,如图所示。

在这里插入图片描述


测试用例

public static void main(String[] args) {
    String str=null;
    //$使用
    str=String.format("格式参数$的使用:%1$d,%2$s", 99,"abc");           
    System.out.println(str);                     
    //+使用
    System.out.printf("显示正负数的符号:%+d与%d%n", 99,-99);
    //补O使用
    System.out.printf("最牛的编号是:%03d%n", 7);
    //空格使用
    System.out.printf("Tab键的效果是:% 8d%n", 7);
    //.使用
    System.out.printf("整数分组的效果是:%,d%n", 9989997);
    //空格和小数点后面个数
    System.out.printf("一本书的价格是:% 50.5f元%n", 49.8);
}

输出结果

格式参数$的使用:99,abc
显示正负数的符号:+99-99
最牛的编号是:007
Tab键的效果是:       7
整数分组的效果是:9,989,997
一本书的价格是:                                          49.80000

日期和事件字符串格式化

在程序界面中经常需要显示时间和日期,但是其显示的 格式经常不尽人意,需要编写大量的代码经过各种算法才得到理想的日期与时间格式。字符串格式中还有%tx转换符没有详细介绍,它是专门用来格式化日期和时 间的。%tx转换符中的x代表另外的处理日期和时间格式的转换符,它们的组合能够将日期和时间格式化成多种格式。

常见日期和时间组合的格式,如图所示。

在这里插入图片描述

测试用例

    public static void main(String[] args) {
        Date date=new Date();                                
        //c的使用
        System.out.printf("全部日期和时间信息:%tc%n",date);        
        //f的使用
        System.out.printf("年-月-日格式:%tF%n",date);
        //d的使用
        System.out.printf("月/日/年格式:%tD%n",date);
        //r的使用
        System.out.printf("HH:MM:SS PM格式(12时制):%tr%n",date);
        //t的使用
        System.out.printf("HH:MM:SS格式(24时制):%tT%n",date);
        //R的使用
        System.out.printf("HH:MM格式(24时制):%tR",date);
    }

输出结果

全部日期和时间信息:星期一 九月 10 10:43:36 CST 2012--日格式:2012-09-10//年格式:09/10/12
HH:MM:SS PM格式(12时制):10:43:36 上午
HH:MM:SS格式(24时制):10:43:36
HH:MM格式(24时制):10:43

定义日期格式的转换符可以使日期通过指定的转换符生成新字符串。这些日期转换符如图所示。

    public static void main(String[] args) {
            Date date=new Date();                                    
            //b的使用,月份简称
            String str=String.format(Locale.US,"英文月份简称:%tb",date);     
            System.out.println(str);                                                                            
            System.out.printf("本地月份简称:%tb%n",date);
            //B的使用,月份全称
            str=String.format(Locale.US,"英文月份全称:%tB",date);
            System.out.println(str);
            System.out.printf("本地月份全称:%tB%n",date);
            //a的使用,星期简称
            str=String.format(Locale.US,"英文星期的简称:%ta",date);
            System.out.println(str);
            //A的使用,星期全称
            System.out.printf("本地星期的简称:%tA%n",date);
            //C的使用,年前两位
            System.out.printf("年的前两位数字(不足两位前面补0):%tC%n",date);
            //y的使用,年后两位
            System.out.printf("年的后两位数字(不足两位前面补0):%ty%n",date);
            //j的使用,一年的天数
            System.out.printf("一年中的天数(即年的第几天):%tj%n",date);
            //m的使用,月份
            System.out.printf("两位数字的月份(不足两位前面补0):%tm%n",date);
            //d的使用,日(二位,不够补零)
            System.out.printf("两位数字的日(不足两位前面补0):%td%n",date);
            //e的使用,日(一位不补零)
            System.out.printf("月份的日(前面不补0):%te",date);
        }

输出结果

英文月份简称:Sep
本地月份简称:九月
英文月份全称:September
本地月份全称:九月
英文星期的简称:Mon
本地星期的简称:星期一
年的前两位数字(不足两位前面补0):20
年的后两位数字(不足两位前面补0):12
一年中的天数(即年的第几天):254
两位数字的月份(不足两位前面补0):09
两位数字的日(不足两位前面补0):10
月份的日(前面不补0):10

和日期格式转换符相比,时间格式的转换符要更多、更精确。它可以将时间格式化成时、分、秒甚至时毫秒等单位。格式化时间字符串的转换符如图所示。

在这里插入图片描述

测试代码

public static void main(String[] args) {
    Date date = new Date();
    //H的使用
    System.out.printf("2位数字24时制的小时(不足2位前面补0):%tH%n", date);
    //I的使用
    System.out.printf("2位数字12时制的小时(不足2位前面补0):%tI%n", date);
    //k的使用
    System.out.printf("2位数字24时制的小时(前面不补0):%tk%n", date);
    //l的使用
    System.out.printf("2位数字12时制的小时(前面不补0):%tl%n", date);
    //M的使用
    System.out.printf("2位数字的分钟(不足2位前面补0):%tM%n", date);
    //S的使用
    System.out.printf("2位数字的秒(不足2位前面补0):%tS%n", date);
    //L的使用
    System.out.printf("3位数字的毫秒(不足3位前面补0):%tL%n", date);
    //N的使用
    System.out.printf("9位数字的毫秒数(不足9位前面补0):%tN%n", date);
    //p的使用
    String str = String.format(Locale.US, "小写字母的上午或下午标记(英):%tp", date);
    System.out.println(str); 
    System.out.printf("小写字母的上午或下午标记(中):%tp%n", date);
    //z的使用
    System.out.printf("相对于GMT的RFC822时区的偏移量:%tz%n", date);
    //Z的使用
    System.out.printf("时区缩写字符串:%tZ%n", date);
    //s的使用
    System.out.printf("1970-1-1 00:00:00 到现在所经过的秒数:%ts%n", date);
    //Q的使用
    System.out.printf("1970-1-1 00:00:00 到现在所经过的毫秒数:%tQ%n", date);
}

输出结果

2位数字24时制的小时(不足2位前面补0:11
2位数字12时制的小时(不足2位前面补0:11
2位数字24时制的小时(前面不补0:11
2位数字12时制的小时(前面不补0:11
2位数字的分钟(不足2位前面补0:03
2位数字的秒(不足2位前面补0:52
3位数字的毫秒(不足3位前面补0:773
9位数字的毫秒数(不足9位前面补0:773000000
小写字母的上午或下午标记():am
小写字母的上午或下午标记(中):上午
相对于GMT的RFC822时区的偏移量:+0800
时区缩写字符串:CST
1970-1-1 00:00:00 到现在所经过的秒数:1347246232
1970-1-1 00:00:00 到现在所经过的毫秒数:1347246232773

Java 枚举(enum) 详解7种常见的用法

用法一:常量

在JDK1.5 之前,我们定义常量都是: public static fianl… 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

Java代码

public enum Color {
RED, GREEN, BLANK, YELLOW
}

用法二:switch

JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。

Java代码

enum Signal {
GREEN, YELLOW, RED
}
public class TrafficLight {
Signal color = Signal.RED;
** public void
change() {
** switch
(color) {
** case** RED:
color = Signal.GREEN;
** break; **
**case **YELLOW:
color = Signal.RED;
** break; **
** case **GREEN:
color = Signal.YELLOW;
** break; **
}
}
}

用法三:向枚举中添加新方法

如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例。

Java代码

public enum Color {
RED(“红色”, 1), GREEN(“绿色”, 2), BLANK(“白色”, 3), YELLO(“黄色”, 4);
// 成员变量
** private** String name;
** private** int index;
// 构造方法
** private Color(String name, int** index) {
** this**.name = name;
** this**.index = index;
}
// 普通方法
** public static String getName(int index) {
for (Color c : Color.values()) {
if (c.getIndex() == index) {
return c.name;
}
}
** return null; **
}
// get set 方法
** public
String getName() {
** return name; **
}
** public void setName(String name) {
** this
.name = name;
}
** public int** getIndex() {
** return index;
}
** public void
setIndex(int index) {
** this**.index = index;
}
}

用法四:覆盖枚举的方法

下面给出一个toString()方法覆盖的例子。

Java代码

public enum Color {
RED(“红色”, 1), GREEN(“绿色”, 2), BLANK(“白色”, 3), YELLO(“黄色”, 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//覆盖方法
@Override
public String toString() {
return this.index+"_"+this.name;
}
}

用法五:实现接口
所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。

Java代码

public interface Behaviour {
void print();
String getInfo();
}
public enum Color implements Behaviour{
RED(“红色”, 1), GREEN(“绿色”, 2), BLANK(“白色”, 3), YELLO(“黄色”, 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//接口方法
@Override
public String getInfo() {
return this.name;
}
//接口方法
@Override
public void print() {
System.out.println(this.index+":"+this.name);
}
}

用法六:使用接口组织枚举
Java代码

public interface Food {
enum Coffee implements Food{
BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
}
enum Dessert implements Food{
FRUIT, CAKE, GELATO
}
}

[java] view plain copy

/**

  • 测试继承接口的枚举的使用(by 大师兄 or 大湿胸。)

  • /
    private static void testImplementsInterface() {
    for (Food.DessertEnum dessertEnum : Food.DessertEnum.values()) {

      System.out.print(dessertEnum + "  ");  
    

    }
    System.out.println();
    //我这地方这么写,是因为我在自己测试的时候,把这个coffee单独到一个文件去实现那个food接口,而不是在那个接口的内部。
    for (CoffeeEnum coffee : CoffeeEnum.values()) {

      System.out.print(coffee + "  ");  
    

    }
    System.out.println();
    //搞个实现接口,来组织枚举,简单讲,就是分类吧。如果大量使用枚举的话,这么干,在写代码的时候,就很方便调用啦。
    //还有就是个“多态”的功能吧,
    Food food = Food.DessertEnum.CAKE;
    System.out.println(food);
    food = CoffeeEnum.BLACK_COFFEE;
    System.out.println(food);
    }
    运行结果

用法七:关于枚举集合的使用

java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而value则可以是任意类型。关于这个两个集合的使用就不在这里赘述,可以参考JDK文档。

关于枚举的实现细节和原理请参考:

参考资料:《ThinkingInJava》第四版

http://softbeta.iteye.com/blog/1185573

我的这篇文章,因为是转载的,可能基本就没有变动,导致被某人踩了一脚。觉得不符合我大师兄的性格。下面我把自己的使用理解给整理一下。

也是因为因为当时刚刚开始学习吧。把平时自以为了解的东西都只是大概了解了一下,说到底,还是自以为了解了,其实转眼就不知道什么是什么了。
出来学习,不习惯看代码怎么能行呢?
下面是我自己的测试代码。

[java] view plain copy

package com.lxk.enumTest;

/**

  • Java枚举用法测试

  • Created by lxk on 2016/12/15

  • /
    public class EnumTest {
    public static void main(String[] args) {

      forEnum();  
      useEnumInJava();  
    

    }

    /**

    • 循环枚举,输出ordinal属性;若枚举有内部属性,则也输出。(说的就是我定义的TYPE类型的枚举的typeName属性)

    • /
      private static void forEnum() {
      for (SimpleEnum simpleEnum : SimpleEnum.values()) {

        System.out.println(simpleEnum + "  ordinal  " + simpleEnum.ordinal());  
      

      }
      System.out.println("------------------");
      for (TYPE type : TYPE.values()) {

        System.out.println("type = " + type + "    type.name = " + type.name() + "   typeName = " + type.getTypeName() + "   ordinal = " + type.ordinal());  
      

      }
      }

      /**

    • 在Java代码使用枚举

    • /
      private static void useEnumInJava() {
      String typeName = “f5”;
      TYPE type = TYPE.fromTypeName(typeName);
      if (TYPE.BALANCE.equals(type)) {

        System.out.println("根据字符串获得的枚举类型实例跟枚举常量一致");  
      

      } else {

        System.out.println("大师兄代码错误");  
      

      }

      }

      /**

    • 季节枚举(不带参数的枚举常量)这个是最简单的枚举使用实例

    • Ordinal 属性,对应的就是排列顺序,从0开始。

    • /
      private enum SimpleEnum {
      SPRING,
      SUMMER,
      AUTUMN,
      WINTER
      }

/** 
 * 常用类型(带参数的枚举常量,这个只是在书上不常见,实际使用还是很多的,看懂这个,使用就不是问题啦。) 
 */  
private enum TYPE {  
    FIREWALL("firewall"),  
    SECRET("secretMac"),  
    BALANCE("f5");  

    private String typeName;  

    TYPE(String typeName) {  
        this.typeName = typeName;  
    }  

    /** 
     * 根据类型的名称,返回类型的枚举实例。 
     * 
     * @param typeName 类型名称 
     */  
    public static TYPE fromTypeName(String typeName) {  
        for (TYPE type : TYPE.values()) {  
            if (type.getTypeName().equals(typeName)) {  
                return type;  
            }  
        }  
        return null;  
    }  

    public String getTypeName() {  
        return this.typeName;  
    }  
}  

Java中Lambda表达式的使用

Lambda表达式的语法:

基本语法: (参数) -> 语句 或 (参数) ->{ 方法体; }

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

基本的Lambda例子


String[] atp = {"Rafael Nadal", "Novak Djokovic",  
       "Stanislas Wawrinka",  
       "David Ferrer","Roger Federer",  
       "Andy Murray","Tomas Berdych",  
       "Juan Martin Del Potro"};  
List<String> players =  Arrays.asList(atp);  
  
// 以前的循环方式  
for (String player : players) {  
     System.out.print(player + "; ");  
}  
  
// 使用 lambda 表达式以及函数操作(functional operation)  
players.forEach((player) -> System.out.print(player + "; "));  

stream API的引用

Java8中提供了Stream对集合操作作出了极大的简化,学习了Stream之后,我们以后不用使用for循环就能对集合作出很好的操作。

一、流的初始化与转换:


Java中的Stream的所有操作都是针对流的,所以,使用Stream必须要得到Stream对象:
  1、初始化一个流:
    Stream stream = Stream.of("a", "b", "c");
  2、数组转换为一个流:
    String [] strArray = new String[] {"a", "b", "c"};
    stream = Stream.of(strArray);
    或者
    stream = Arrays.stream(strArray);//使用Arrays 中的 stream() 方法,将数组转成流
  3、集合对象转换为一个流(Collections):
    List<String> list = Arrays.asList(strArray);//数组转化成集合
    stream = list.stream();
 4、使用Collection下的 stream()parallelStream() 方法
 List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
 5、 使用 BufferedReader.lines() 方法,将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);

二、流的操作:

对于Java中的lambda表达式的操作,可以归类和整理如下:

中间操作:

过滤 filter(也就是相当于筛选自己想要的元素)
去重 distinct
排序 sorted
截取 limit、skip
转换 map/flatMap

map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
其他 peek

终止操作

循环 forEach
计算 min、max、count、 average
匹配 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny
汇聚 reduce
收集器 toArray collect

//定义一个学生类
package com.isoft;
import java.math.BigDecimal;
public class Student {
    public Student(String stuName, int age, BigDecimal score, int clazz) {
        this.stuName = stuName;
        this.age = age;
        this.score = score;
        this.clazz = clazz;
    }

    private String stuName;
    private int age;
    private BigDecimal score;
    private int clazz;

    public String getStuName() {
        return stuName;
    }
    public void setStuName(String stuName) {
        this.stuName = stuName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public BigDecimal getScore() {
        return score;
    }
    public void setScore(BigDecimal score) {
        this.score = score;
    }
    public int getClazz() {
        return clazz;
    }
    public void setClazz(int clazz) {
        this.clazz = clazz;
    }
}


package com.isoft;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>();
        studentList.add(new Student("黎  明", 20, new BigDecimal(80), 1));
        studentList.add(new Student("郭敬明", 22, new BigDecimal(90), 2));
        studentList.add(new Student("明  道", 21, new BigDecimal(65.5), 3));
        studentList.add(new Student("郭富城", 30, new BigDecimal(90.5), 4));
        studentList.add(new Student("刘诗诗", 20, new BigDecimal(75), 1));
        studentList.add(new Student("成  龙", 60, new BigDecimal(88), 5));
        studentList.add(new Student("郑伊健", 60, new BigDecimal(86), 1));
        studentList.add(new Student("刘德华", 40, new BigDecimal(81), 1));
        studentList.add(new Student("古天乐", 50, new BigDecimal(83), 2));
        studentList.add(new Student("赵文卓", 40, new BigDecimal(84), 2));
        studentList.add(new Student("吴奇隆", 30, new BigDecimal(86), 4));
        studentList.add(new Student("言承旭", 50, new BigDecimal(68), 1));
        studentList.add(new Student("郑伊健", 60, new BigDecimal(86), 1));
        studentList.add(new Student("黎  明", 20, new BigDecimal(80), 1));
        studentList.add(new Student("李连杰", 65, new BigDecimal(86), 4));
        studentList.add(new Student("周润发", 69, new BigDecimal(58), 1));
        studentList.add(new Student("徐若萱", 28, new BigDecimal(88), 6));
        studentList.add(new Student("许慧欣", 26, new BigDecimal(86), 8));
        studentList.add(new Student("陈慧琳", 35, new BigDecimal(64), 1));
        studentList.add(new Student("关之琳", 45, new BigDecimal(50), 9));
        studentList.add(new Student("温碧霞", 67, new BigDecimal(53), 2));
        studentList.add(new Student("林青霞", 22, new BigDecimal(56), 3));
        studentList.add(new Student("李嘉欣", 25, new BigDecimal(84), 1));
        studentList.add(new Student("彭佳慧", 26, new BigDecimal(82), 5));
        studentList.add(new Student("陈紫涵", 39, new BigDecimal(88), 1));
        studentList.add(new Student("张韶涵", 41, new BigDecimal(90), 6));
        studentList.add(new Student("梁朝伟", 58, new BigDecimal(74), 1));
        studentList.add(new Student("梁咏琪", 65, new BigDecimal(82), 7));
        studentList.add(new Student("范玮琪", 22, new BigDecimal(83), 1));
        //forEach:代表循环当前的list ,下面的例子是循环打印出student的名字
        studentList.stream().forEach(x-> System.out.print(x.getStuName()+" "));
        System.out.println();
        //filter根据条件过滤当前的数据,获得分数大于80的学生名称
        studentList.stream().filter(t -> t.getScore().compareTo(new BigDecimal(80)) > 0).
                forEach(x -> System.out.print(x.getStuName()+" "));
        System.out.println();
//distinct去除重复数据
studentList.stream().distinct().forEach(x -> System.out.print(x.getStuName()+" "));
        System.out.println();
//sorted 单条件排序和多条件排序
 studentList.stream().sorted(Comparator.comparing(Student::getScore)).forEach(x -> System.out.print(x.getStuName()+" "));
        System.out.println();
 //多条件排序
 studentList.stream().sorted(Comparator.comparing(Student::getScore).thenComparing(Student::getStuName)).
         forEach(x -> System.out.print(x.getStuName()+" "));
        System.out.println();
//group 的使用
System.out.println(studentList.stream().collect(Collectors.groupingBy(x->x.getAge(),Collectors.counting())));
        System.out.println();
        //limit、skip跳过多少,取多少个元素,可以根据当前的数据进行分页
        studentList.stream().skip(10).limit(5).forEach(x -> System.out.print(x.getStuName()+" "));
        System.out.println();
//具体的分页
        int pageIndex=1;
        int pageSize=5;
        studentList.stream().skip((pageIndex-1)*pageSize).limit(pageSize).forEach(x -> System.out.print(x.getStuName()+" "));
        System.out.println();
        //map/flatMap  map是一个转换的工具,提供很多转换的方法,mapToInt,mapToDouble
        studentList.stream().map(Student::getScore).forEach(x -> System.out.print(x+" "));
//上面的结果是输出当前的所有同学的得分。
        System.out.println();
        //flatMap是一个可以把子数组的值放到数组里面, 下面的实例是把所有的名字都拆开成一个新的数组
        studentList.stream().flatMap(x-> Arrays.stream(x.getStuName().split(""))).
                forEach(x -> System.out.print(x));
        System.out.println();
        //min、max、count、 average 一组常用的统计函数:
        studentList.stream().max(Comparator.comparing(x -> x.getAge())).ifPresent(x-> System.out.println(x.getAge()));
        studentList.stream().min(Comparator.comparing(x -> x.getAge())).ifPresent(x-> System.out.println(x.getAge()));
        System.out.println(studentList.stream().count());
        studentList.stream().mapToDouble(x -> x.getScore().doubleValue()).average().ifPresent(x-> System.out.println(x));
        System.out.println("-------------------------------------------------------------------");
        //anyMatch、noneMatch、 allMatch、 findFirst、 findAny
        //anyMatch: 操作用于判断Stream中的是否有满足指定条件的元素。如果最少有一个满足条件返回true,否则返回false。
        //noneMatch: 与anyMatch相反。allMatch是判断所有元素是不是都满足表达式。
        //findFirst: 操作用于获取含有Stream中的第一个元素的Optional,如果Stream为空,则返回一个空的Optional。若Stream并未排序,可能返回含有Stream中任意元素的Optional。
        //findAny: 操作用于获取含有Stream中的某个元素的Optional,如果Stream为空,则返回一个空的Optional。由于此操作的行动是不确定的,其会自由的选择Stream中的任何元素。在并行操作中,在同一个Stram中多次调用,可能会不同的结果。在串行调用时,都是获取的第一个元素, 默认的是获取第一个元素,并行是随机的返回。
        System.out.println(studentList.stream().anyMatch(r -> r.getStuName().contains("伟")));
        System.out.println(studentList.stream().allMatch(r -> r.getStuName().contains("伟")));
        System.out.println(studentList.stream().noneMatch(r -> r.getStuName().contains("伟")));
        System.out.println(studentList.stream().findFirst());
        System.out.println(studentList.stream().findAny());

        for (int i=0;i<10;i++)
        {
            System.out.println(studentList.stream().parallel().findAny().get().getStuName());
        }
        System.out.println("-------------------------------------------------------------------");
        //toArray、collecttoArray和collect是两个收集器,toArray是把数据转换成数组,collect是转成其他的类型。这里就不在讨论了。
        System.out.println(studentList.stream().collect(Collectors.groupingBy(x->x.getAge(),Collectors.counting())));
        System.out.println("-------------------------------------------------------------------");

          }
}
运行结果:
黎  明 郭敬明 明  道 郭富城 刘诗诗 成  龙 郑伊健 刘德华 古天乐 赵文卓 吴奇隆 言承旭 郑伊健 黎  明 李连杰 周润发 徐若萱 许慧欣 陈慧琳 关之琳 温碧霞 林青霞 李嘉欣 彭佳慧 陈紫涵 张韶涵 梁朝伟 梁咏琪 范玮琪 
郭敬明 郭富城 成  龙 郑伊健 刘德华 古天乐 赵文卓 吴奇隆 郑伊健 李连杰 徐若萱 许慧欣 李嘉欣 彭佳慧 陈紫涵 张韶涵 梁咏琪 范玮琪 
黎  明 郭敬明 明  道 郭富城 刘诗诗 成  龙 郑伊健 刘德华 古天乐 赵文卓 吴奇隆 言承旭 郑伊健 黎  明 李连杰 周润发 徐若萱 许慧欣 陈慧琳 关之琳 温碧霞 林青霞 李嘉欣 彭佳慧 陈紫涵 张韶涵 梁朝伟 梁咏琪 范玮琪 
关之琳 温碧霞 林青霞 周润发 陈慧琳 明  道 言承旭 梁朝伟 刘诗诗 黎  明 黎  明 刘德华 彭佳慧 梁咏琪 古天乐 范玮琪 赵文卓 李嘉欣 郑伊健 吴奇隆 郑伊健 李连杰 许慧欣 成  龙 徐若萱 陈紫涵 郭敬明 张韶涵 郭富城 
关之琳 温碧霞 林青霞 周润发 陈慧琳 明  道 言承旭 梁朝伟 刘诗诗 黎  明 黎  明 刘德华 彭佳慧 梁咏琪 古天乐 范玮琪 李嘉欣 赵文卓 吴奇隆 李连杰 许慧欣 郑伊健 郑伊健 徐若萱 成  龙 陈紫涵 张韶涵 郭敬明 郭富城 
{65=2, 67=1, 35=1, 69=1, 39=1, 40=2, 41=1, 45=1, 50=2, 20=3, 21=1, 22=3, 25=1, 58=1, 26=2, 28=1, 60=3, 30=2}

吴奇隆 言承旭 郑伊健 黎  明 李连杰 
黎  明 郭敬明 明  道 郭富城 刘诗诗 
80 90 65.5 90.5 75 88 86 81 83 84 86 68 86 80 86 58 88 86 64 50 53 56 84 82 88 90 74 82 83 
黎  明郭敬明明  道郭富城刘诗诗成  龙郑伊健刘德华古天乐赵文卓吴奇隆言承旭郑伊健黎  明李连杰周润发徐若萱许慧欣陈慧琳关之琳温碧霞林青霞李嘉欣彭佳慧陈紫涵张韶涵梁朝伟梁咏琪范玮琪
69
20
29
78.17241379310344
-------------------------------------------------------------------
true
false
false
Optional[com.isoft.Student@5f184fc6]
Optional[com.isoft.Student@5f184fc6]
陈慧琳
刘诗诗
陈慧琳
陈慧琳
陈慧琳
陈慧琳
陈慧琳
陈慧琳
陈慧琳
陈慧琳
-------------------------------------------------------------------
{65=2, 67=1, 35=1, 69=1, 39=1, 40=2, 41=1, 45=1, 50=2, 20=3, 21=1, 22=3, 25=1, 58=1, 26=2, 28=1, 60=3, 30=2}
-------------------------------------------------------------------

Java中的两种比较器的区别

Java中有两种比较器,Comparable、Comparator,用来比较两个对象的大小的。
可以把Comparable理解为内部比较器,而Comparator是外部比较器,写法的区别:

Comparable是在实体类的内部去实现Comparable接口的compareTo()方法进行比较。 对于Comparable接口来说,它往往是进行比较类需要实现的接口,它仅包含一个有compareTo()方法,只有一个参数,返回值为int,返回值大于0表示对象大于参数对象;小于0表示对象小于参数对象;等于0表示两者相等**//比较器是定义在实体类中。**

/*使用Comparable比较器,实现Comparable接口,并重写compareTo方法,
通过调用Collections.sort(),或者用Arrays.tostring()来实现排序*/
//学生类
package com.isoft;
public class Student implements Comparable<Student> {
    public Student(String stuName, int age, double score, int clazz) {
        this.stuName = stuName;
        this.age = age;
        this.score = score;
        this.clazz = clazz;
    }
    private String stuName;
    private int age;
    private double score;
    private int clazz;

    public String getStuName() {
        return stuName;
    }
    public void setStuName(String stuName) {
        this.stuName = stuName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
    public int getClazz() {
        return clazz;
    }
    public void setClazz(int clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "Student{" + "stuName='" + stuName + '\'' +   ", age=" + age +    ", score=" + score +  ", clazz=" + clazz +  '}';
    }

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;//这是简化的编写格式
        /*
       if(this.age>o.age){
           return 1;
       }else if(this.age<o.age){
           return -1;
        }else {
           return 0;
       }
         此方法也可以*/
    }
}
//主函数
package com.isoft;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class Main {
    public static void main(String[] args)throws Exception {
    Student data[]=new Student[]{
          new Student("张三",18,67,3) ,
            new Student("李四",19,77,4) ,
            new Student("王二",17,56,2)
    };
        Arrays.sort(data);
        System.out.println(Arrays.toString(data));//Arrays.toString()数组转字符串
        /*在实际项目中,如果想要把数组中的内容打印出来,直接使用toString方法只会打印出数组的地址,因此需要使用Arrays的toString方法,
        可以从其内部实现中看出来,该方法支持入参可以是long,float,double,int,boolean,byte,object型的数组。*/
        System.out.println();
        ArrayList<Student> arrayList=new ArrayList<>();
        arrayList.add(new Student("唐僧",107,98,7));
        arrayList.add(new Student("猪八戒",997,88,9));
        arrayList.add(new Student("沙僧",877,78,6));
        Collections.sort(arrayList);
        for (Student student : arrayList) {
            System.out.print(student+",");
        }
    }
}
//运行结果
[Student{stuName='王二', age=17, score=56.0, clazz=2}, Student{stuName='张三', age=18, score=67.0, clazz=3}, Student{stuName='李四', age=19, score=77.0, clazz=4}]

Student{stuName='唐僧', age=107, score=98.0, clazz=7},Student{stuName='沙僧', age=877, score=78.0, clazz=6},Student{stuName='猪八戒', age=997, score=88.0, clazz=9},

对于Comparator接口来说,它的实现者被称为比较器,它包含一个compare()方法,有两个参数,返回值与Comparable的compareTo()方法一样,不同之处是Comparator接口一般不会被集合元素类所实现,而是单独实现或者匿名内部类方式实现

Arrays.sort(T[],Comparator<? super T> c);
Collections.sort(List list,Comparator<? super T> c)
;

//比较器的定义没在实体类中,是一个单独的比较器类。

//学生类
package com.isoft;
public class Student  {
    public Student(String stuName, int age, double score, int clazz) {
        this.stuName = stuName;
        this.age = age;
        this.score = score;
        this.clazz = clazz;
    }

    private String stuName;
    private int age;
    private double score;
    private int clazz;

    public String getStuName() {
        return stuName;
    }
    public void setStuName(String stuName) {
        this.stuName = stuName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
    public int getClazz() {
        return clazz;
    }
    public void setClazz(int clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "Student{" + "stuName='" + stuName + '\'' +   ", age=" + age +    ", score=" + score +  ", clazz=" + clazz +  '}';
    }
    }
//主类
package com.isoft;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Main {
    public static void main(String[] args)throws Exception {
    Student data[]=new Student[]{
          new Student("张三",18,67,3) ,
            new Student("李四",19,77,4) ,
            new Student("王二",17,56,2)
    };
        Arrays.sort(data, new Comparator<Student>() {
            @Override
            //比较班级
            public int compare(Student o1, Student o2) {
                return o1.getClazz()-o2.getClazz();
            }
        });
        System.out.println(Arrays.toString(data));//Arrays.toString()数组转字符串
        /*在实际项目中,如果想要把数组中的内容打印出来,直接使用toString方法只会打印出数组的地址,因此需要使用Arrays的toString方法,
        可以从其内部实现中看出来,该方法支持入参可以是long,float,double,int,boolean,byte,object型的数组。*/
        System.out.println();
        ArrayList<Student> arrayList=new ArrayList<>();
        arrayList.add(new Student("唐僧",107,98,7));
        arrayList.add(new Student("猪八戒",997,88,9));
        arrayList.add(new Student("沙僧",877,78,6));
        arrayList.sort(new Comparator<Student>() {
           @Override
           //比较字符串
           public int compare(Student o1, Student o2) {
               return o1.getStuName().compareTo(o2.getStuName());//StuName为String,String类的compareTo()方法是用来比较两个字符串的字典顺序,这里的字典顺序指的是ASCII码表中的字符顺序。
           }
       });
        for (Student student : arrayList) {
            System.out.print(student+",");
        }
        System.out.println();
       arrayList.sort(new Comparator<Student>() {
           @Override
           //比较分数
           public int compare(Student o1, Student o2) {
               return (int) (o1.getScore()-o2.getScore());
           }
       });
        for (Student student : arrayList) {
            System.out.print(student+",");
        }
    }
}
//运行结果
[Student{stuName='王二', age=17, score=56.0, clazz=2}, Student{stuName='张三', age=18, score=67.0, clazz=3}, Student{stuName='李四', age=19, score=77.0, clazz=4}]

Student{stuName='唐僧', age=107, score=98.0, clazz=7},Student{stuName='沙僧', age=877, score=78.0, clazz=6},Student{stuName='猪八戒', age=997, score=88.0, clazz=9},
Student{stuName='沙僧', age=877, score=78.0, clazz=6},Student{stuName='猪八戒', age=997, score=88.0, clazz=9},Student{stuName='唐僧', age=107, score=98.0, clazz=7},

反射入门

什么是反射机制?

在方法区存在这么一些对象,叫做类对象,他们表述了我们写的所有的类,当我们new对象时会根据这些类对象,并调用其构造方法为我们创建实例

首先大家应该先了解两个概念,编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行。JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为ava语言的反射机制

筒单的说一个类有多个组成部分,例如:成员变量,方法,构造方法等。反射就是加载类,并解剖出类的各个组成部分。

Java反射机制主要提供了以下功能:

  • 在运行时判断任意个对象所属的类

  • 在运行时构造任意一个类的对象

  • 在运行时判断任意一个类所具有的成员变量和方法;

  • 在运行时调用任意一个对象的方法;生成动态代理

new和反射创建有什么区别呢?

new: 静态编译,在编译期就将模块编译进来,执行该字节码文件,所有的模块都被加载;

**反射:动态编译,**编译期没有加载,等到模块被调用时才加载;


获取CLASS类对象的方法

1.使用对象
Member member=new Member();
Class memberclass=member.getClass();

2.使用类
Class memberclass=Member.class;

3.使用全类名
Class memberclass=Class.forName("包名+类名");

class类一部分常用方法总结

字段(field),通常叫做“类成员”,或 "类成员变量”,有时也叫“域”,理解为“数据成员”,用来承载数据的

修饰符和类型方法描述
static Class<?>Class.forName(String name, boolean initialize, ClassLoader loader)和 Class.forName(String className)name表示的是类的全名;initialize表示是否初始化类。loader表示载入时使用的类载入器,获取CLASS类对象
Annotation[]getAnnotations()返回此元素上 存在的注释。
ClassLoadergetClassLoader()返回类的类加载器。
字段getDeclaredField​(String name)返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
字段[]getDeclaredFields()返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。
方法getDeclaredMethod​(String name, 类<?>… parameterTypes)返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
方法[]getDeclaredMethods()返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法
字段getField​(String name)返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
字段[]getFields()返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
方法getMethod​(String name, 类<?>… parameterTypes)返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
方法[]getMethods()返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
URLgetResource​(String name)查找具有给定名称的资源。
InputStreamgetResourceAsStream​(String name)查找具有给定名称的资源。
StringtoString()将对象转换为字符串。
booleanisArray()确定此 类对象是否表示数组类。
booleanisInterface()确定指定的 类对象是否表示接口类型。

理解类名 对象名 =new 类名(参数);

=左边:创建一个类的对象

=右边:调用这个类的构造函数初始化对象,类名(参数)这个是构造函数,用来做初始化的没有参数默认调用无参构造函数。

Java中new 类名()是什么意思?

只调用一次构造方法,不要调用其它方法或者属性,可以这样写。

package com.isoft;
class Book{
    private String title;
    int count;
    public Book(String title) {
        System.out.println(title);
    }
    public void count(){
        System.out.println(count);
    }
}

public class Main  {
    public static void main(String[] args) {
      new Book("嘻嘻");
      Book book=new Book("哈哈");
      book.count=30;
      book.count();

    }
}
运行结果:
嘻嘻
哈哈
30


this()与super()使用详解

this三大作用:

this调用属性、调用方法、利用this表示当前对象。

this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针

this的用法在java中大体可以分为3种:

1.普通的直接引用

this相当于是指向当前对象本身。

2.形参与成员名字重名,用this来区分:

package com.isoft;
class Person {
    private int age = 10;
    public Person() {
        System.out.println("初始化年龄:" + age);
    }

    public int getAge(int age) {
        this.age = age;
        return this.age;
    }
}

public class Main {

    public static void main(String[] args) {
        Person harry = new Person();
        System.out.println("Harry's age is " + harry.getAge(12));
    }
}
//可以看到,这里age是getAge成员方法的形参,this.age是Person类的成员变量。

3.引用构造函数

this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。

package com.isoft;
class Person {
    private  String name;
    private int age;
    /**无参构造**/
    public Person() {
        System.out.println("***一个新的Person类对象实例化了。****");
    }
    /**单参构造**/
    public Person(String name) {
        this();
        this.name = name;
    }
    /**双参构造**/
    public Person(String name, int age) {
        this(name);
        this.name = name;
        this.age = age;
    }
    public void tell(){
        System.out.println("姓名:"+this.name+",年龄:"+this.age);
    }
}

public class Main {

    public static void main(String[] args) {
        Person per = new Person("张三",12);
        per.tell();
    }
}

super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。

super三种用法:

1.普通的直接引用

与this类似,super相当于是指向当前对象的父类,这样就可以用super.xxx来引用父类的成员。

2.子类中的成员变量或方法与父类中的成员变量或方法同名

package com.isoft;
class  Country {
    String name;
    void  value() {
        name =  "China" ;
    }
}

public class Main extends Country{
    String name;
    /**方法覆写:当子类中定义与父类中方法名称,返回值类型,参数类型及其参数个数完全相同**/
    @Override
    void  value() {
        name =  "Shanghai" ;
        super .value();       //调用父类的方法
        System.out.println(name);
        System.out.println( super.name);
    }
    public static void main(String[] args) {
      Main m=new Main();
      m.value();
    }
}

运行结果:
Shanghai
China

3.引用构造函数

super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。

package com.isoft;
class Person {
    public static void prt(String s) {
        System.out.println(s);
    }
    Person() {
        prt("父类·无参数构造方法: " + "A Person.");
    } //构造方法(1)
    Person(String name) {
        prt("父类·含一个参数的构造方法: " + "A person's name is " + name);
    } //构造方法(2)
}

public class Main extends Person {
    /** 调用父类构造方法(1)**/
    Main() {
        super();
        prt("子类·调用父类”无参数构造方法“: " + "A chinese coder.");
    }
    /** 调用父类具有相同形参的构造方法(2)**/
    Main(String name) {
        super(name);
        prt("子类·调用父类”含一个参数的构造方法“: " + "his name is " + name);
    }
    /** 调用具有相同形参的构造方法(3)**/
    Main(String name, int age) {
        this(name);
        prt("子类:调用子类具有相同形参的构造方法:his age is " + age);
    }


    public static void main(String[] args) {
        Main cn = new Main();
        cn = new Main("coders");
        cn = new Main("coders", 18);
    }
}
运行结果:
父类·无参数构造方法: A Person.
子类·调用父类”无参数构造方法“: A chinese coder.
父类·含一个参数的构造方法: A person's name is coders
子类·调用父类”含一个参数的构造方法“: his name is coders
父类·含一个参数的构造方法: A person's name is coders
子类·调用父类”含一个参数的构造方法“: his name is coders
子类:调用子类具有相同形参的构造方法:his age is 18

Static关键字

此关键字可以用于全局属性和全局方法的声明,特点:可以避免对象实例化的限制,在没有实例化对象的时候可以直接进行此类的结构访问。

1.static修饰成员方法

static修饰的方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都必须依赖具体的对象才能够被调用。


2.static修饰成员变量

static修饰的变量也称为静态变量,静态变量和非静态变量的区别是:静态变量被所有对象共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化存在多个副本,各个对象拥有的副本互不影响

static成员变量的初始化顺序按照定义的顺序进行初始化。

package com.isoft;
class Book{
    private String title;
    private static int count=0;

    public Book(String title) {
        this.title = title;
    }

    public Book() {
        this("第"+(count++)+"图书");
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public static int getCount() {
        return count;
    }

    public static void setCount(int count) {
        Book.count = count;
    }
}

public class Main  {
    public static void main(String[] args) {
        System.out.println( new Book("Java开发实战经典").getTitle());
        System.out.println(new Book().getTitle());
        System.out.println(new Book("Spring实战开发").getTitle());
        System.out.println(new Book().getTitle());
        System.out.println( new Book("Spring微架构实战开发").getTitle());
        System.out.println(new Book().getTitle());
    }
}

3.static修饰代码块(静态代码块)

1,在非主类中定义:

静态代码块优先于构造代码块执行,而且不管实例化多少个对象,静态代码块中的代码只执行一次。

package com.isoft;
class Person{
    public Person() {
        System.out.println("【构造方法】Person类构造方法执行");
    }
    static {
        System.out.println("【静态块】静态块执行");
    }
    /*构造块形式: 只用{}包围的代码块。
     作用:给所有对象进行统一的初始化。
     执行顺序:创建对象时,会先执行构造代码块,然后再执行构造函数。
     构造代码块和构造函数的比较:
     相同点:都是给对象进行初始化的。
     不同点:构造代码块是给所有对象进行统一的初始化。构造函数是给对应的对象初始化。**/
    {
        System.out.println("【构造块】Person构造块执行");
    }
}

public class Main  {
    public static void main(String[] args) {
      new Person();
      new Person();
    }
}
运行结果:
【静态块】静态块执行
【构造块】Person构造块执行
【构造方法】Person类构造方法执行
【构造块】Person构造块执行
【构造方法】Person类构造方法执行

2.在主类中定义:

主类中定义的静态代码优先于主方法执行

顺序:【主类静态块】静态块执行>主方法>【非主类静态块】静态块执行>【构造块】Person构造块执行>【构造方法】Person类构造方法执行

package com.isoft;
class Person{
    public Person() {
        System.out.println("【构造方法】Person类构造方法执行");
    }
    static {
        System.out.println("【非主类静态块】静态块执行");
    }
    /*构造块形式: 只用{}包围的代码块。
     作用:给所有对象进行统一的初始化。
     执行顺序:创建对象时,会先执行构造代码块,然后再执行构造函数。
     构造代码块和构造函数的比较:
     相同点:都是给对象进行初始化的。
     不同点:构造代码块是给所有对象进行统一的初始化。构造函数是给对应的对象初始化。**/
    {
        System.out.println("【构造块】Person构造块执行");
    }
}

public class Main  {
    static {
        System.out.println("【主类静态块】静态块执行");
    }
    public static void main(String[] args) {
        System.out.println("主方法");
        new Person();
    }
}

运行结果:
【主类静态块】静态块执行
主方法
【非主类静态块】静态块执行
【构造块】Person构造块执行
【构造方法】Person类构造方法执行

为什么要用多线程?

有一下几点,首先,随着cpu核心数的增加,计算机硬件的并行计算能力得到提升,而同一个时刻一个线程只能运行在一个cpu上,那么计算机的资源被浪费了,所以需要使用多线程。其次,也是为了提高系统的响应速度,如果系统只有一个线程可以执行,那么当不同用户有不同的请求时,由于上一个请求没处理完,那么其他的用户必定需要在一个队列中等待,大大降低响应速度,所以需要多线程。

多线程的应用场景

程序中出现需要等待的操作,比如网络操作、文件IO等,可以利用多线程充分使用处理器资源,而不会阻塞程序中其他任务的执行
程序中出现可分解的大任务,比如耗时较长的计算任务,可以利用多线程来共同完成任务,缩短运算时间
程序中出现需要后台运行的任务,比如一些监测任务、定时任务,可以利用多线程来完成

Java多线程实现

在java中,如果要想实现多线程,那么就必须要依靠一个线程的主体类 ,这个主体类可以通过继承Thread类,实现Runnable接口或实现Callable接口来完成定义

Thread类实现多线程

**优点:**实现简单,只需实例化继承类的实例,即可使用线程
**缺点:**扩展性不足,Java是单继承的语言,如果一个类已经继承了其他类,就无法通过这种方式实现自定义线程、

Thread方法:

变量和类型方法描述
static ThreadcurrentThread()返回对当前正在执行的线程对象的引用。
StringgetName()返回此线程的名称。
longgetId()
返回此Thread的标识符。
static voidsleep​(long millis)设置线程休眠的毫秒数,时间一到自动唤醒
static voidsleep​(long millis, int nanos)设置线程休眠的毫秒数与纳秒数,时间一到自动唤醒
booleanisInterrupted()测试此线程是否已被中断。
voidinterrupt()中断此线程。
voidjoin()强制执行某线程,完成后在执行其它线程
voidjoin​(long millis)此线程最多等待 millis毫秒后强制执行。
static voidyield()让其它线程先执行,自己等待下次
voidsetName​(String name)设置线程名称
voidsetPriority​(int newPriority)设置线程优先级(MIN一PRIORITY = 1,最低优先级,NORM一PRIORITY = 5,普通优先级,默认值,MAX一PRIORITY = 10,最高优先级)
package com.isoft;

class MyThread extends Thread{
    private String title;
    public MyThread(String title) {
        this.title = title;
    }

    @Override
    public void run() {
       for(int x=0;x<10;x++){
           System.out.println(this.title+"运行,x="+x);
       }
    }
}

public class Main {

    public static void main(String[] args) {
        new MyThread("线程A").start();
        new MyThread("线程B").start();
        new MyThread("线程C").start();
    }
}
运行结果(随机抽取)
线程A运行,x=0
线程B运行,x=0
线程C运行,x=0
线程B运行,x=1
线程A运行,x=1
线程B运行,x=2
线程C运行,x=1
线程C运行,x=2
线程C运行,x=3
线程C运行,x=4
线程C运行,x=5
线程B运行,x=3
线程A运行,x=2
线程A运行,x=3
线程A运行,x=4
线程B运行,x=4
线程C运行,x=6
线程B运行,x=5
线程C运行,x=7
线程C运行,x=8
线程C运行,x=9
线程A运行,x=5
线程A运行,x=6
线程A运行,x=7
线程B运行,x=6
线程A运行,x=8
线程B运行,x=7
线程A运行,x=9
线程B运行,x=8
线程B运行,x=9

为什么多线程的启动不直接使用run()方法而必须使用Thread类中start()方法呢?

start()方法启动线程后,整个线程处于就绪状态,等待虚拟机调度, 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。期间虚拟机是分时间片轮番调用各个线程体的。

run()方法启动是当作普通方法的方式调用,这里虚拟机不会线程调度,虚拟机会执行这个方法直到结束后自动退出。

任何情况下,只要定义了多线程,多线程的启动永远只有一种方案:Thread类中的start()方法。

Runnable接口实现多线程

**优点:**扩展性好,可以在此基础上继承其他类,实现其他必需的功能
对于多线程共享资源的场景,具有天然的支持,适用于多线程处理一份资源的场景
缺点:构造线程实例的过程相对繁琐一点

虽然可以通过Thread类的继承来实现多线程的定义,但是在Java程序里面对于继承永远都是****存在有单继承局限的,所以在Java里面又提供有第二种多线程的主体定义结构形式:实现java.lang.Runnable接口

** 但是此时由于不在继承Thread父类了,那么对于此时的MyThread类中也就不在支持有start()这个继承方法,可是不使用Thread.start()方法是无法进行多线程启动的,那么这个时候就需要去观察一下Thread类所提供的构造方法了。**

  • 构造方法:public Thread(Runnable target);
    构造器描述
    Thread()分配新的 Thread对象。
    Thread​(Runnable target)分配新的 Thread对象。
    Thread​(Runnable target, String name)分配新的 Thread对象。
    Thread​(String name)分配新的 Thread对象。
    Thread​(ThreadGroup group, Runnable target)分配新的 Thread对象。
    Thread​(ThreadGroup group, Runnable target, String name)分配新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,并且属于 group引用的线程组。
    Thread​(ThreadGroup group, Runnable target, String name, long stackSize)分配新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,并且属于 group引用的线程组,并具有指定的 堆栈大小 。
    Thread​(ThreadGroup group, Runnable target, String name, long stackSize, boolean inheritThreadLocals)分配新的Thread对象,使其具有target作为其运行对象,具有指定的name作为其名称,属于group引用的线程组,具有指定的stackSize ,并且如果inheritThreadLocals是true ,则继承inheritable thread-local变量的初始值。
    Thread​(ThreadGroup group, String name)分配新的 Thread对象。
package com.isoft;

class MyThread implements Runnable {//线程主体类
    private String title;
    public MyThread(String title) {
        this.title = title;
    }
    @Override
    public void run() {//线程的主体方法
        for(int x = 0; x < 10 ; x++) {
            System.out.println(this.title + "运行,x = " + x);
        }
    }
}

public class Main {

    public static void main(String[] args) {
        Thread threadA = new Thread(new MyThread("线程A"));
        Thread threadB = new Thread(new MyThread("线程B"));
        Thread threadC = new Thread(new MyThread("线程C"));
        threadA.start();
        threadB.start();
        threadC.start();
    }
}
运行结果
线程C运行,x = 0
线程C运行,x = 1
线程B运行,x = 0
线程C运行,x = 2
线程B运行,x = 1
线程C运行,x = 3
线程C运行,x = 4
线程B运行,x = 2
线程C运行,x = 5
线程B运行,x = 3
线程C运行,x = 6
线程A运行,x = 0
线程A运行,x = 1
线程A运行,x = 2
线程A运行,x = 3
线程A运行,x = 4
线程B运行,x = 4
线程B运行,x = 5
线程A运行,x = 5
线程A运行,x = 6
线程A运行,x = 7
线程A运行,x = 8
线程A运行,x = 9
线程C运行,x = 7
线程B运行,x = 6
线程B运行,x = 7
线程B运行,x = 8
线程C运行,x = 8
线程B运行,x = 9
线程C运行,x = 9


Callable实现多线程

**优点:**扩展性好
支持多线程处理同一份资源
具备返回值以及可以抛出受检查异常
**缺点:**相较于实现Runnable接口的方式,较为繁琐

从最传统的开发来讲如果要进行多线程的实现肯定依靠的就是Runnable,但是Runnable接口有一个缺点:当线程执行完毕后,我们无法获取一个返回值,所以从JDK1.5之后就提出了一个新的线程实现接口:java.util.concurrent.Callable接口。

Callbale定义的时候可以设置一个泛型,此泛型的类型就是返回数据的类型,这样的的好处是可以避免向下转行所带来的安全隐患。

线程类定义完成后如果要进行多线程的启动依然需要通过Thread类实现。所以此时可以通过FutureTask类实现Callable接口与Thread类之间的联系,因为FutureTask类是Runnable接口的子类,并且可以利用FutureTask的方法获取Callable接口中call()方法的返回值。

构造方法:

构造器描述
FutureTask​(Runnable runnable, V result)创建一个 FutureTask ,在运行时执行给定的 Runnable ,并安排 get在成功完成时返回给定的结果。
FutureTask​(Callable callable)创建一个 FutureTask ,在运行时将执行给定的 Callable 。

常用方法:

变量和类型方法描述
protected voiddone()当此任务转换到状态 isDone (无论是正常还是通过取消),调用受保护的方法。
Vget()取得线程操作结果
protected booleanrunAndReset()执行计算而不设置其结果,然后将此未来重置为初始状态,如果计算遇到异常或被取消则无法执行此操作
StringtoString()返回此FutureTask的字符串表示形式。
protected voidset​(V v)将此future的结果设置为给定值,除非已设置或已取消此未来。
package com.isoft;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThread2 implements Callable<String> {
    @Override
    public String call() throws Exception {
        for ( int x = 0 ; x < 10 ; x ++ ) {
            System.out.println("******线程执行,x = " + x);
        }
        return "线程执行完毕!";
    }
}

public class Main {

    public static void main(String[] args) {
        FutureTask futureTask = new FutureTask(new MyThread2());
        new Thread(futureTask).start();
        try {
            System.out.println("线程返回值:" + futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
运行结果:
******线程执行,x = 0
******线程执行,x = 1
******线程执行,x = 2
******线程执行,x = 3
******线程执行,x = 4
******线程执行,x = 5
******线程执行,x = 6
******线程执行,x = 7
******线程执行,x = 8
******线程执行,x = 9
线程返回值:线程执行完毕!

Java实现线程同步

线程同步是指若干个线程对象并行并进行资源访问时实现的资源处理的保护操作。线程同步可以解决多个线程都执行了某个程序,造成数据不安全的问题。

使用synchronized关键字

1.修饰方法

package com.isoft;
class MyThread implements Runnable{
    private int ticket=10;
    @Override
    public void run() {
      while (sale()){
          ;
      }
    }
    public synchronized boolean sale(){
        if(ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"卖票,ticket="+ticket--);
            return true;
        }else{
            System.out.println("****票已经卖光了****");
           return false;
        }
    }
}

public class Main  {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        new Thread(mt,"售票员A").start();
        new Thread(mt,"售票员B").start();
        new Thread(mt,"售票员C").start();
    }
}
运行结果:
售票员A卖票,ticket=10
售票员A卖票,ticket=9
售票员A卖票,ticket=8
售票员B卖票,ticket=7
售票员A卖票,ticket=6
售票员C卖票,ticket=5
售票员C卖票,ticket=4
售票员C卖票,ticket=3
售票员C卖票,ticket=2
售票员C卖票,ticket=1
****票已经卖光了****
****票已经卖光了****
****票已经卖光了****

2.修饰代码块

package com.isoft;
class MyThread implements Runnable{
    private int ticket=10;
    @Override
    public void run() {
        while (true){
     /*此处this指的是进入此代码块的线程对象,如果t1进来了,那么锁住t1,若t1时间片结束了,
   t2走到此处也只能在上一句代码处等待t1获得了时间片后执行完synchronized锁住的所有代码,
   t2才能进去执行,若去掉synchronized(this),则t1和t2随时都可以进来执行此段代码中的任何一步,
   时间到了另一个接着进来执行**/
            synchronized (this){
                if(ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"卖票,ticket="+ticket--);
                }else{
                    System.out.println("****票已经卖光了****");
                    break;
                }
            }
        }
    }
}

public class Main  {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        new Thread(mt,"售票员A").start();
        new Thread(mt,"售票员B").start();
        new Thread(mt,"售票员C").start();
    }
}
运行结果:
售票员A卖票,ticket=10
售票员C卖票,ticket=9
售票员C卖票,ticket=8
售票员C卖票,ticket=7
售票员C卖票,ticket=6
售票员C卖票,ticket=5
售票员C卖票,ticket=4
售票员B卖票,ticket=3
售票员B卖票,ticket=2
售票员B卖票,ticket=1
****票已经卖光了****
****票已经卖光了****
****票已经卖光了****

Java数据库JDBC——connection,statement,prepareStatement,ResultSet接口的用法和解释

IDEA项目连接MySQL数据库

JDBC一共有7个步骤:加载驱动、建立连接、写SQL语句、得到statement对象、执行SQL语句得到结果集、执行结果集、关闭资源。

加载驱动

第一步:将驱动类的jar添加到项目中:

1.在项目中新键一个jars的文件夹

右键项目名称——>新建(New)——>目录(Directory)——>名为jars

在这里插入图片描述

2将jar包粘贴到此文件夹中

在这里插入图片描述

在这里插入图片描述

3.选择文件(File)——>项目结构(Project Structrue)——>库(Libraries)

——>点击+——>Java——>选择当前项目下的jars文件下的我们刚复制过去的jar包——>点击确定(ok)——>点击应用(apply),再点确定(ok)


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

第二步:在构造方法中加载驱动类

Class.forName("com.mysql.jdbc.Driver");//加载数据库

建立连接

Connection conn = null;//保存数据库连接

conn= DriverManager.getConnection(url,user,password);

DriverManager常用方法:

变量和类型方法描述
static Streamdrivers()使用当前调用者可以访问的所有当前加载的JDBC驱动程序检索Stream。
static ConnectiongetConnection​(String url, String user, String password)尝试建立与给定数据库URL的连接。
static voidprintln​(String message)将消息打印到当前JDBC日志流。

检验数据库是否连接成功

Spackage com.isoft;

import java.sql.Connection;
import java.sql.DriverManager;

public class Main {
    private static String url="jdbc:mysql://localhost:3306/db?serverTimezone =Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false";
    private static String user="root";
    private static String password="root";
    public static void main(String[] args)throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn=null;
        conn= DriverManager.getConnection(url,user,password);
        System.out.println(conn);
        conn.close();
    }
}
运行结果:
com.mysql.jdbc.JDBC4Connection@a269e2
由于本程序连接成功,所以输出的conn对象不为null.

java和mysql连接的url问题

String url = "jdbc:mysql://localhost:3306/supermarket?serverTimezone = 
Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false"

supermarket:数据库的名字
localhost:数据库所在服务器的IP(localhost本地)
3306:数据库的端口号
jdbc:mysql:// 是指JDBC连接方式
useUnicode:是否使用Unicode字符集
characterEncoding:当useUnicode设置为true时,指定字符编码。
serverTimezone:时区,一般选Asia/Shanghai即可,不选会报错
MySQL 8.0 以上版本不需要建立 SSL 连接的,需要显示关闭。



当创建好连接之后就可以向数据库发送sql语句了

写sql语句

SELECT * FROM goods;

获取执行sql语句的对象 Statement,prepareStatement,执行结果集Resultset

Connection常用方法

变量和类型方法描述
voidclose()立即释放此 Connection对象的数据库和JDBC资源,而不是等待它们自动释放。
StatementcreateStatement()创建一个 Statement对象,用于将SQL语句发送到数据库。
StatementcreateStatement​(int resultSetType, int resultSetConcurrency)创建一个 Statement对象,该对象将生成具有给定类型和并发性的 ResultSet对象。
PreparedStatementprepareStatement​(String sql)创建一个 PreparedStatement对象,用于将参数化SQL语句发送到数据库。
PreparedStatementprepareStatement​(String sql, int resultSetType, int resultSetConcurrency)创建一个 PreparedStatement对象,该对象将生成具有给定类型和并发性的 ResultSet对象。

Statement常用方法

变量和类型方法描述
voidaddBatch​(String sql)将给定的SQL命令添加到此 Statement对象的当前命令列表中。
voidclose()立即释放此 Statement对象的数据库和JDBC资源,而不是等待它自动关闭时发生。
booleanexecute​(String sql)execute方法应该仅在语句能返回多个ResultSet对象、多个更新计数或ResultSet对象与更新计数的组合时使用。
ResultSetexecuteQuery​(String sql)方法executeQuery()用户产生单个结果集的语句,这个方法只能用来执行select语句
intexecuteUpdate​(String sql)方法executeUpdate()用于执行insert,delete,update等SQL语句,executeupdate返回的值是一个整数,表示受影响的行数

PrepareStatement常用方法

变量和类型方法描述
voidaddBatch()
向此 PreparedStatement对象的一批命令添加一组参数。
booleanexecute()在此 PreparedStatement对象中执行SQL语句,该对象可以是任何类型的SQL语句。
ResultSetexecuteQuery()执行此 PreparedStatement对象中的SQL查询,并返回查询生成的 ResultSet对象。
intexecuteUpdate()执行在该SQL语句PreparedStatement对象,它必须是一个SQL数据操纵语言(DML)语句,比如INSERT , UPDATE或DELETE ; 或者不返回任何内容的SQL语句,例如DDL语句。
voidsetDate​(int parameterIndex, Date x)使用运行应用程序的虚拟机的默认时区将指定参数设置为给定的 java.sql.Date值。
voidsetDouble​(int parameterIndex, double x)将指定参数设置为给定的Java double值。
voidsetFloat​(int parameterIndex, float x)将指定参数设置为给定的Java float值。
voidsetInt​(int parameterIndex, int x)将指定参数设置为给定的Java int值。
voidsetLong​(int parameterIndex, long x)将指定参数设置为给定的Java long值。
voidsetTime​(int parameterIndex, Time x)将指定参数设置为给定的 java.sql.Time值。
voidsetString​(int parameterIndex, String x)将指定参数设置为给定的Java String值。
voidsetObject​(int parameterIndex, Object x)使用给定对象设置指定参数的值。

Resultset常用方法

变量和类型方法描述
voidclose()立即释放此 ResultSet对象的数据库和JDBC资源,而不是等待它自动关闭时发生。
voiddeleteRow()从此 ResultSet对象和基础数据库中删除当前行。
booleannext()将光标从当前位置向前移动一行。
intgetInt​(int columnIndex)写第几列(1,2…n)
intgetInt​(String columnLabel)写列名
doublegetDouble​(int columnIndex)写第几列(1,2…n)
doublegetDouble​(String columnLabel)写列名
DategetDate​(int columnIndex)写第几列(1,2…n),获得在数据库里是日期类型的数据,返回值类型是java.sql.Date。
DategetDate​(String columnLabel)写列名,获得在数据库里是日期类型的数据,返回值类型是java.sql.Date。
longgetLong​(int columnIndex)写第几列(1,2…n)
longgetLong​(String columnLabel)写列名
StringgetString​(int columnIndex)写第几列(1,2…n),获得在数据库里是CHAR 或 VARCHAR等字符串类型的数据,返回值类型是
String
StringgetString​(String columnLabel)写列名,获得在数据库里是CHAR 或 VARCHAR等字符串类型的数据,返回值类型是
String
package com.isoft;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Main {
    private static String url="jdbc:mysql://localhost:3306/supermarket?serverTimezone =Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false";
    private static String user="root";
    private static String password="root";
    public static void main(String[] args)throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn=null;
        conn= DriverManager.getConnection(url,user,password);
       String sql="SELECT * FROM goods;";
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery(sql);
        //执行sql语句,将查询到的内容保存到rs里面
        while (rs.next()) {
/*next()是将结果集光标从当前位置向后移一行,结果集光标最初位于第一行之前;第一次调用 next 方法使第一行成为当前行;第二次调用使第二行成为
当前行,依此类推。如果新的当前行有效,则返回 true;如果不存在下一行,则返回false。*/
            System.out.print( rs.getInt(1)+" ");
            System.out.print(rs.getString(2)+" ");
            System.out.print(rs.getInt(3)+" ");
            System.out.print(rs.getFloat(4)+" ");
            System.out.print( rs.getInt(5)+" ");
            System.out.println(rs.getDate(6)+" ");//推荐用这种,不容易错

            /* System.out.print( rs.getInt("id")+" ");
            System.out.print(rs.getString("gname")+" ");
            System.out.print(rs.getInt("count")+" ");
            System.out.print(rs.getFloat("price")+" ");
            System.out.print( rs.getInt("uid")+" ");
            System.out.println(rs.getDate("createtime")+" ");*/

            /* System.out.print( rs.getInt("id")+" ");
            System.out.print(rs.getString(2)+" ");
            System.out.print(rs.getInt(3)+" ");
            System.out.print(rs.getFloat("price")+" ");
            System.out.print( rs.getInt("uid")+" ");
            System.out.println(rs.getDate("createtime")+" ");*/
        }
        conn.close();
    }
}
运行结果:
1 苹果 45 10.0 1 2021-01-12 
2 水杯 81 27.5 1 2021-01-06 
4 杯子 2 3.5 1 2021-01-06 
5 杯子 3 7.5 1 2021-01-06 
6 水杯 5 13.8 1 2021-01-06 
7 牛奶 6 99.9 1 2021-01-06 
8 奥特曼 200 16.0 1 2021-01-06 
9 哇哈哈 14 2.0 1 2021-01-06 
11 奥里给 6 1000.0 1 2021-01-13 

当我们使用只是查询的时候一般可以使用Statement就可以,但是我们日常的业务逻辑处理中不单单只是查询,还会有DML操作,当我们执行DML操作时,为了防止sql注入,就不可以使用Statement,我们需要使用PreparedStatement接口来代替他了,PreparedStatement是Statement的子接口。
使用PreparedStatement接口的一些好处:
1.提高执行语句的性能
2.可读性和可维护性更好
3.简化Statement中的操作
4.安全性更好(可预防sql注入)

package com.isoft;

import java.sql.*;
import java.util.Scanner;

public class Main {
    private static String url="jdbc:mysql://localhost:3306/supermarket?serverTimezone =Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false";
    private static String user="root";
    private static String password="root";
    public static void main(String[] args)throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn=null;
        conn= DriverManager.getConnection(url,user,password);
        String sql="insert into goods(gname,count,price,uid,createtime) values(?,?,?,?,NOW())";
        PreparedStatement preparedStatement=conn.prepareStatement(sql);
        Scanner s=new Scanner(System.in);
        System.out.println("请输入商品名称");
        Object temp=s.next();
        System.out.println("请输入商品数量");
        Object temp1=s.next();
        System.out.println("请输入商品单价");
        Object temp2=s.next();
        System.out.println("请输入操作者编号");
        Object temp3=s.next();
        preparedStatement.setObject(1,temp);
        preparedStatement.setObject(2,temp1);
        preparedStatement.setObject(3,temp2);
        preparedStatement.setObject(4,temp3);
        System.out.println(preparedStatement.executeUpdate());

      String sql1="SELECT * FROM goods;";
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery(sql1);
        while (rs.next()) {
            System.out.print( rs.getInt(1)+" ");
            System.out.print(rs.getString(2)+" ");
            System.out.print(rs.getInt(3)+" ");
            System.out.print(rs.getFloat(4)+" ");
            System.out.print( rs.getInt(5)+" ");
            System.out.println(rs.getDate(6)+" ");

        }
        conn.close();
    }
}
运行结果:
请输入商品名称
大白兔
请输入商品数量
100
请输入商品单价
0.3
请输入操作者编号
123
1
1 苹果 45 10.0 1 2021-01-12 
2 水杯 81 27.5 1 2021-01-06 
4 杯子 2 3.5 1 2021-01-06 
5 杯子 3 7.5 1 2021-01-06 
6 水杯 5 13.8 1 2021-01-06 
7 牛奶 6 99.9 1 2021-01-06 
8 奥特曼 200 16.0 1 2021-01-06 
9 哇哈哈 14 2.0 1 2021-01-06 
11 奥里给 6 1000.0 1 2021-01-13 
12 豆干 100 1.0 123 2021-02-03 
13 辣条 100 0.5 123 2021-02-03 
14 大白兔 100 0.3 123 2021-02-03 

优化上述代码:

建立三个类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7a3WUAja-1615362150982)(attachment:4ed87bcaf6e25b3f8f6b777d3fdd4a8e)]

DBTool类:
package com.isoft;
import java.sql.*;
public class DBTool {
    private String url = "jdbc:mysql://localhost:3306/supermarket?serverTimezone =Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false";
    private String userName = "root";
    private String pwd = "root";
    private Connection conn;
    private PreparedStatement prep;

    public DBTool() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection(url, userName, pwd);
    }

    public int update(String sql, Object ... objs) throws Exception {
        //切记Object空格...空格objs
/*可变参数的使用方法,在方法中定义未知数量的参数
*格式为:修饰符 返回值类型 方法名(参数类型…变量名){}
* 如果未知个数的参数类型不同,那么可以把可变参数类型定义为Object
* 修饰符 返回值类型 方法名(Object … 变量名){}
* */
        prep = conn.prepareStatement(sql);
        for (int i = 0; i < objs.length; i++) {
            prep.setObject(i + 1, objs[i]);
        }
        return prep.executeUpdate();
    }
        public void show()throws Exception{
            String sql1 = "SELECT * FROM goods;";
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(sql1);
            while (rs.next()) {
                System.out.print(rs.getInt(1) + " ");
                System.out.print(rs.getString(2) + " ");
                System.out.print(rs.getInt(3) + " ");
                System.out.print(rs.getFloat(4) + " ");
                System.out.print(rs.getInt(5) + " ");
                System.out.println(rs.getDate(6) + " ");
            }
        }
        public void close ()throws Exception {
            if (prep != null && !prep.isClosed()) {
                prep.close();
            }
            if (conn != null && !conn.isClosed()) {
                conn.close();
            }
        }
    }

GoodsImpl类:
package com.isoft;
import java.util.Scanner;
public class GoodsImpl {
    public int add() throws Exception {
        String sql = "insert into goods(gname,count,price,uid,createtime) values(?,?,?,?,NOW())";
        Scanner s = new Scanner(System.in);
        System.out.println("请输入商品名称");
        Object temp = s.next();
        System.out.println("请输入商品数量");
        Object temp1 = s.next();
        System.out.println("请输入商品单价");
        Object temp2 = s.next();
        System.out.println("请输入操作者编号");
        Object temp3 = s.next();
        DBTool tool=new DBTool();
        int str=tool.update(sql,temp,temp1,temp2,temp3);
        tool.close();
        return str;
    }
}
Main类:
package com.isoft;
public class Main {
    public static void main(String[] args) throws Exception {
        GoodsImpl g=new GoodsImpl();
        g.add();
        DBTool tool=new DBTool();
        tool.show();
        tool.close();
    }
}
运行结果:
请输入商品名称
阿尔卑斯
请输入商品数量
1000
请输入商品单价
0.5
请输入操作者编号
123
1 苹果 45 10.0 1 2021-01-12 
2 水杯 81 27.5 1 2021-01-06 
4 杯子 2 3.5 1 2021-01-06 
5 杯子 3 7.5 1 2021-01-06 
6 水杯 5 13.8 1 2021-01-06 
7 牛奶 6 99.9 1 2021-01-06 
8 奥特曼 200 16.0 1 2021-01-06 
9 哇哈哈 14 2.0 1 2021-01-06 
11 奥里给 6 1000.0 1 2021-01-13 
12 豆干 100 1.0 123 2021-02-03 
13 辣条 100 0.5 123 2021-02-03 
14 大白兔 100 0.3 123 2021-02-03 
15 水果 100 2.0 123 2021-02-03 
16 金丝猴 100 2.0 123 2021-02-03 
17 阿尔卑斯 1000 0.5 123 2021-02-03 -
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值