内部类

概念

  1. 在类的内部声明的类称之为内部类
  2. 在类的三个地方可以声明内部类

    1. 方法里面可以定义内部类,不可以使用public修饰(局部内部类)

      1. public static void main(String[] args) {
      2. //在方法里面定义内部类,不可以使用public修饰
      3. class MyInner1{
      4. public void t1(){
      5. System.out.println("我是方法中的内部类");
      6. }
      7. }
      8. }
    2. 方法外部,类的内部定义内部类,可以用public修饰,但是如果要在外部使用是使用static进行修饰(成员内部类

      1. public class MyOut {
      2. public static void main(String[] args) {
      3. //main方法
      4. }
      5. //在类中,方法之外定义内部类,可以用public修饰
      6. public static class MyInner2{
      7. public void t1(){
      8. System.out.println("我是类中的内部类");
      9. }
      10. }
      11. }
    3. 在类的外部声明,不可以用public修饰(外部内部类) ```java public class MyOut {

} //在类的外面,也可以定义内部类 class MyInner3{ public void t1(){ System.out.println(“我是类外的内部类”); } }

  1. 总结:在方法的里面和在类的外部声明内部类,不可以使用public。在方法的外面,类的里面声明内部类可以使用public,但是如果外面使用此内部类,则需要给内部类加上static
  2. <a name="UtJoU"></a>
  3. ### 内部类的创建和调用
  4. 1. 所有的内部类都可以在声明的类中创建
  5. ```java
  6. public class MyOut {
  7. public void main() {
  8. //在方法里面定义内部类,不可以使用public修饰
  9. class MyInner1{
  10. public void t1(){
  11. System.out.println("我是方法中的内部类");
  12. }
  13. }
  14. MyInner1 myInner1 = new MyInner1();
  15. MyInner2 myInner2 = new MyInner2();
  16. MyInner3 myInner3 = new MyInner3();
  17. }
  18. //在类中,方法之外定义内部类,可以用public修饰
  19. public class MyInner2{
  20. public void t1(){
  21. System.out.println("我是类中的内部类");
  22. }
  23. }
  24. }
  25. //在类的外面,也可以定义内部类
  26. class MyInner3{
  27. public void t1(){
  28. System.out.println("我是类外的内部类");
  29. }
  30. }
  1. 类中和方法外声明的内部类可以在其他类中创建(创建的时候必须带上外部类的名称)

MyOut.MyInner2 inner=new MyOut.MyInner2();

关于static对内部类的修饰

  1. static只能修饰成员内部类
  2. 主要作用就是防止成员内部类包含外部类的默认this指针引用
  3. 还可以使得成员内部类被其他的外部类创建(new)
  4. 如果成员内部类的外部类的方法被static进行修饰了,成员内部类也必须使用static修饰之后才使用使用

    局部内部类的使用

    局部内部类要使用所在方法的局部变量的时候,具备变量需要使用final进行修饰

    匿名内部类

  5. 主要针对的是成员内部类的定义,可以直接使用new对象进行创建,而不需要专门声明成员内部类

  6. 必须要对类继承,或者是接口才可以创建对象

    1. public class Test002 {
    2. public static void main(String[] args) {
    3. class MyInner{
    4. public void run(){
    5. System.out.println("我是局部内部类");
    6. }
    7. };
    8. MyInner inner = new MyInner();
    9. inner.run();
    10. //简化局部内部的定义,就是匿名内部类,
    11. //必须对类的继承,或者是接口的实现才可以使用
    12. class MySon extends MyFather{
    13. public int getMoney(){
    14. return this.money;
    15. }
    16. }
    17. new MySon().getMoney();
    18. new Object(){
    19. public int getMoney(){
    20. return 1;
    21. }
    22. }.getMoney();
    23. //继承的内部类的写法
    24. new MyFather(){
    25. public int getMoney(){
    26. return this.money;
    27. }
    28. }.getMoney();
    29. /////对接口局部类内部的声明
    30. class MyClass implements MyInterface{
    31. @Override
    32. public void run() {
    33. System.out.println("局部内部类实现的接口的调用");
    34. }
    35. };
    36. //创建成员内部类
    37. new MyClass().run();
    38. //可以实现多个方法
    39. new MyInterface(){
    40. @Override
    41. public void run() {
    42. System.out.println("匿名内部类的写法");
    43. }
    44. }.run();
    45. //lambdb表单的写法,必须要有引用类型,只能用于接口
    46. //接口只可以一个方法
    47. MyInterface myInterface = ()->{
    48. System.out.println("lambda表达式的写法");
    49. };
    50. myInterface.run();;
    51. }
    52. }

    面向对象语法梳理

  7. 所有的java代码都写在src中

  8. 在创建java代码之前,必须要创建包,包的规则是公司域名倒置
  9. 创建.java文件,首字母必须大写,只能是字母
    1. class:类
    2. interface:接口
    3. enum:枚举
    4. annotation:注解
  10. java文件组成部分
    1. 指定所在的包名

package com.java2022.entity;

  1. 如果要使用其他包下类或者接口,需要使用import将类或者接口进行导入

import com.java2022.entity.MyFather;

启示:类名的全称应该= 包名 + 类名java.lang这个包下面的类默认被导入了,所以String不导包也可以

  1. 每个java文件可以声明多个类、接口、枚举和注解,但是有且仅有一个public 接口、类、枚举、注解和文件名称一致
    1. 类文件:使用class定义的java文件
  2. 类的名称必须和文件名一致,而是public修饰的
    1. 不可以使用static,可以使用final(最后的)(不可以被继承)和abstract(抽象)(有abstract修饰的方法)
    2. 可以在类名的后面使用extends继承一个类
    3. 在类名后面使用implements(在extends后面)实现多个接口(逗号分隔)
    4. 类的最后必须要有一个括号{}
  3. 定义成员变量(属性)
    1. 可以不用初始化,具备默认值
      1. 基本类型的默认值
        1. char,int,short,byte,long都是0
        2. double,float都是0.0
        3. boolean是false
      2. 其他所有应用类型默认值都是null
    2. 引用类,可以使用new 初始化创建对象
    3. 变量在赋值的时候,可以使用类的静态方法,和对象的方法,可以支持三元表达的
    4. 所有的成员变量都需要进行封装
    5. 如果用static修饰的变量是放在静态区(也叫方法区),可以被当前所有new出的对象共享
  4. 构造方法:和类的名字一致,没有返回值,可以重载

    1. 默认自带一个无参的构造方法,在重载之后,默认的构造方法会失效,建议一般都将默认的构造方法补上
    2. 一个对象在被创建的时候必须要调用自己构造方法 和 父类的构造方法

      1. 如果父类构造方法被重载了,没有默认无参构造方法,那么子类在创建是不符合规范的
      2. 必须要在子类的构造方法中的第一行通过super调用父类构造方法 ```java public class MyClass3 extends MyFather{

      public MyClass3(){

      1. super(10);

      } public MyClass3(int a){

      1. this();
      2. // super(10);

      } } ```

    3. this是指向当前类创建的对象的引用,this指向是自己,super指向是父类

      1. this/super.属性/方法
      2. this/super()调用构造方法
  5. 一般方法
    1. 基本语法

<修饰符> <返回类型> <方法名称>(<参数类型1> <形参名称1>,<参数类型2> <形参名称2>){
//具体的代码
//如果返回类型不是void,那么必须要写return <对应返回类型的具体值>
}

  1. 2. 方法的分类
  2. 1. 普通方法:必须要通过new出对象之后才可以被调用
  3. 1. static修饰的方法:可以通过类名称.方法名称直接调用
  4. 1. public static void main(String[] args):一个java程序的**入口方法**
  5. 3. 方法的参数可以是没有,也可以是多个,还可以是可变
  6. 3. 方法的返回只有一个类型,不可以有多个
  1. 方法中的代码
    1. 可以声明具备变量(基本和引用类型)
    2. 对于引用类型,主要是通过new 创建对象进行使用
      1. 可以不使用引用类型,直接通过new创建对象
      2. 基本类型不可以直接使用,必须要声明
    3. 在调用一个方法有返回值的时候,可以直接放入到另外一个方法的形参中,对象也是如此

setPerson(new Person(“x”,123));
System.out.println(t2());

  1. 4. 方法类内部也是可以声明类(局部内部类)
  2. 1. 局部的内部类可以直接使用外部的成员变量和方法,因为默认情况下局部内部类保存一个外部类的引用
  3. 1. 局部的内部类可以调用外部方法的局部变量,但是这个变量必须要使用final进行修饰
  1. public void test1(){
  2. final int bb =0;//方法的局部变量
  3. class MyObject{
  4. public void testA1(){
  5. MyClass3.this.aa++;
  6. int cc = bb;
  7. cc++;
  8. }
  9. }
  10. }
  1. 5. 匿名内部类本质上对局部内部类的简化写法
  2. - 必须要是继承或者是实现
  1. //匿名内部类
  2. new MyInterface002(){
  3. @Override
  4. public void run() {
  5. System.out.println("代码运行");
  6. }
  7. }.run();
  1. - lambda表达式
  1. //当匿名内部类实现的是接口并且,接口只有一个方法的时候可以使用lambda表达式
  2. MyInterface002 t1= ()->{
  3. System.out.println("代码lambda运行了");
  4. };
  5. t1.run();
  1. 可以编写成员内部类
    1. 基本要求和局部内部类似
    2. 区别
      1. 可以使用public修饰,可以使得成员内部类可以被其他类调用
      2. 可以使用static修饰,可以坚决默认保存外部类的this引用的问题,以防止内存泄露
  2. 类中可以直接使用static编写代码块
    1. 当前类被虚拟机器加载的时候,会直接调用
      1. 使用new创建的时候
      2. 使用类加载器加载的时候Class.forName
      3. 调用静态方法或者属性的时候
    2. 在其他类中声明是不会调用的

      Object

      概念

  1. Object是所有的类、接口的根类,任何类没有明确实现extends Object也默认继承了
  2. 那么所有的类有可以使用或者是重写Object的方法
  3. Object声明引用可以指向任意对象
  4. 使用object引用可以接受方法的任意返回结果类型
  5. 使用object类型的形参,可以接收任意传入类型实参

    1. //使用object类型的形参,可以接收任意传入类型实参
    2. public void setName(Object myTeacher) {
    3. }
    4. //作为返回结果类型,可以接收任意返回类型
    5. public Object getStudent(){
    6. return new MyStudent();
    7. }

    常用方法

  6. getClass(),获取当前对象的类的类

  7. hashCode(),可以获取当前对象的hashcode,用于在一个hash集合中判断两个对象是否一致
  8. toString(),默认返回对象的字符串表述,可以通过重写toString方法返回对象中所有属性的值,便于测试
  9. equals(), 主要可以通过重写来判断两个对象的值是否一致,如果使用==判断是两个对象地址是否一致 ```java @Override public boolean equals(Object obj) {
    1. //1判断两个对象是否是同一个引用
    2. if(this==obj) {
    3. return true;
    4. }
    5. //2判断obj是否null
    6. if(obj==null) {
    7. return false;
    8. }
    9. //3判断是否是同一个类型
    // if(this.getClass()==obj.getClass()) { //
    // }
    1. //intanceof 判断对象是否是某种类型
    2. if(obj instanceof Student) {
    3. //4强制类型转换
    4. Student s=(Student)obj;
    5. //5比较熟悉
    6. if(this.name.equals(s.getName())&&this.age==s.getAge()) {
    7. return true;
    8. }
  1. }
  2. return false;
  3. }
  1. 5. finalize()方法,在对象被垃圾回收器,回收的时候调用方法,一般用于判断当前对象是否被内存释放了
  2. 1. 重写finalize方法
  3. ```java
  4. public class MyObject {
  5. @Override
  6. protected void finalize() throws Throwable {
  7. super.finalize();
  8. System.out.println("当前对象被内存释放");
  9. }
  10. }
  1. 在代码释放内存

    1. public static void main(String[] args) {
    2. MyObject obj = new MyObject();
    3. //将obj设置null
    4. obj = null;
    5. System.gc();//呼叫垃圾回收期,释放内存
    6. System.out.println("程序结束了");
    7. }

    封装类

    概念

  1. 所有的基本类型,都不可以设置为null,都会有一个默认的值。
  2. 这样在某些情况下无法判断,那么为了可以将基本类型设置为null,则需要一个类将基本类型进行封装,才可以判断是否为空
  3. 在jdk中已经为所有的基本类型,设置封装类(首字母上大写,除了整数Integer和字符Character) | 基本类型 | 包装类 | | —- | —- | | byte | Byte | | short | Short | | int | Integer | | long | Long | | float | Float | | double | Double | | char | Character | | boolean | Boolean |

应用

  1. 解决无法使用null进行判断问题

    1. int i=100;
    2. /*
    3. if(i==null){//不能使用
    4. }*/
    5. //将一个基本类型转换成了封装类型
    6. Integer integer = new Integer(i);
    7. //就可以直接使用封装的引用类型判断是否为null
    8. if(integer ==null){
    9. System.out.println("当前integer为"+null);
    10. }else{
    11. //从封装类型中获取具体的值
    12. System.out.println("存储的内容:"+integer.intValue());
    13. }
  2. 自动装拆箱

    1. //直接将基本类型的值赋值给封装类型的引用
    2. //jdk会自动执行装箱
    3. Integer integer2 = i;
    4. //可以直接从封装引用中,将存储的基本类型
    5. //拆箱出来赋值给基本类型
    6. int result = integer2;
  3. 可以实现类型之间转换

    1. //从string类型转换成数值类型
    2. String aa= "11";
    3. //封装类中有自带的方法可以进行转换
    4. //将string转换成int
    5. Integer a = Integer.parseInt(aa);
    6. //先转换,然后再自定拆箱
    7. double d = Double.parseDouble("5.3");

    缓冲区

  4. Java预先创建了256个常用的整数包装类型对象(范围是-128-127)。

  5. 在实际应用当中,对已创建的对象进行复用。
  6. 我们来看下基础类型的包装类的缓存,Integer 默认缓存 -128 ~ 127 区间的值,Long 和 Short 也是缓存了这个区间的值,Byte 只能表示 -127 ~ 128 范围的值,全部缓存了,Character 缓存了 0 ~ 127 的值。Float 和 Double 没有缓存的意义。

    1. Integer num1 = 10;
    2. Integer num2 = 10;
    3. System.out.println(num1==num2);//因为是缓冲区所以相同
    4. int n1 = 10;
    5. int n2 = 10;
    6. Integer num3 = 1000;
    7. Integer num4 = 1000;
    8. System.out.println(num3==num4);//因为不是缓冲区,所以不相同
    9. System.out.println(num3.intValue()==num4.intValue());//将封装类中具体存储的值获取进行比较

    字符串类

    主要是对char数组的封装,用于存储字符串

    String常用方法

  7. String中字符的遍历

    1. String str = "你好同学";
    2. for (int i=0;i<str.length();i++){
    3. char ch = str.charAt(i);
    4. System.out.println(ch);
    5. }
  8. 判断字符串是否存在

    1. String str = "查找str首次出现的下标,存在,则返回该下标;不存在,则返回-1";
    2. //判断字符串是否存在
    3. boolean result = str.contains("返回1");
    4. System.out.println(result);
  9. 去掉前后的空格

    1. String username = " 张 三 "; //= scanner.next();
    2. username = username.trim();
    3. if(username.equals("张三")){
    4. System.out.println("用户存在");
    5. }else{
    6. System.out.println("对不起,没有这个用户");
    7. }
  10. 比较后缀名

    1. String str ="天龙八部.mp4";
    2. String str2 = "绿色.mp3";
    3. if(str.endsWith(".mp4")){
    4. System.out.println("这个是电影");
    5. }
    6. if(str2.endsWith(".mp3")){
    7. System.out.println("这个是音乐");
    8. }
  11. 替换

    1. String str = "张三是一个好人";
    2. str = str.replace("好人","坏人");
    3. System.out.println(str);
  12. 根据某个字符将字符串进行切割

    1. String tel = "010-444-8888";
    2. String[] result = tel.split("-");
    3. for (int i=0;i<result.length;i++){
    4. System.out.println(result[i]);
    5. }

    字符串常量池

  13. 为了防止字符串在使用的使用重复创建内存,还使用的一个常量池

  14. 当使用=赋值的时候,会直接从常量池中获取字符串,如果不存在则在常量 池创建一个常量,并且将地址返回
  15. 当使用new创建对象的时候会分别在堆和常量中创建两个对象
  16. intern是将当前对象中存储数据在常量池中的地址进行返回

    1. String str1 ="小明";//存储在常量池中
    2. String str2 = "小明";//存储在常量中
    3. //如果new,会在堆里面存储一份,也会常量池里面存一份
    4. String str3 = new String("小明");
    5. System.out.println(str1==str2);
    6. System.out.println(str2==str3);
    7. //intern是从常量池冲获取存储数据的地址
    8. System.out.println(str2==str3.intern());

    StringBuffer和StringBuilder(重点)

  17. String在使用字符串拼接的时候,每次拼接都会创建一个新的内存空间,这样性能低下又浪费内存

    1. String sql = "select";
    2. sql += " *"; //都需要创建的一个新的空间
    3. sql += "from";//都需要创建的一个新的空间
    4. sql += " tb_user";//都需要创建的一个新的空间
    5. sql += " where ";//都需要创建的一个新的空间
  18. 所以jdk推出了两个类StringBuffer和StringBuilder,在字符串拼接的时候不会创建新的内存空间,在拼接完毕之后在一次性创建

    1. StringBuffer sb = new StringBuffer();
    2. //使用stringbuffer添加字符串
    3. sb.append("select");
    4. sb.append(" *");
    5. sb.append("from");
    6. //在拼接完毕之后,统一创建一个新的字符串空间
    7. String str = sb.toString();
  19. StringBuffer和StringBuilder

    1. StringBuffer使用为了保证线程的安全,使用了synchronized关键字会多线访问这个块内存保证线程的安全,但是会影响性能
    2. StringBuilder没有使用锁,虽然不安全,但是性能高
    3. 如果没有多线程,优先使用StringBuilder;如果使用了多线程的话那么要使用StringBuilder
  20. 速度测试

    1. String str="";
    2. StringBuilder sb = new StringBuilder();
    3. long start = System.currentTimeMillis();
    4. for(int i=0;i<100000;i++){
    5. str+="小王";
    6. }
    7. long end = System.currentTimeMillis();
    8. System.out.println("String时间:"+(end-start));
    9. ///////////////////////////////////////
    10. start = System.currentTimeMillis();
    11. for(int i=0;i<100000;i++){
    12. sb.append("小王");
    13. }
    14. String result = sb.toString();
    15. end = System.currentTimeMillis();
    16. System.out.println("StringBuilder时间:"+(end-start));

    BigDecimal

  21. 主要保证计算小数的精度

  22. 由于java基本类型都是使用二进制进行计算,所以会出现一些不可控的精度问题 ```java double d1 = 1.0; double d2 = 0.9;

System.out.println(d1-d2);//0.09999999999999998

  1. 3. 提供BigDecimal来解决这个问题(创建的BigDecimal要使用字符串)
  2. ```java
  3. double d2 = 0.9;
  4. System.out.println(d1-d2);
  5. //要传入的是string类型
  6. //BigDecimal b1 = new BigDecimal("1.0");
  7. //BigDecimal b2 = new BigDecimal("0.9");
  8. BigDecimal b1 = new BigDecimal(d1+"");
  9. BigDecimal b2 = new BigDecimal(d2+"");
  10. //b1+b2
  11. //b1.add(b2); +
  12. //b1.subtract; -
  13. //b1.multiply(); *
  14. // b1.divide(); /
  15. BigDecimal b3 = b1.subtract(b2);
  16. System.out.println(b3);
  1. 除不尽的解决

    1. BigDecimal b1 = new BigDecimal("1.0");
    2. BigDecimal b2 = new BigDecimal("3");
    3. //除不尽会报错
    4. //System.out.println(b1.divide(b2));
    5. //第一个参数是要被除的对象
    6. //第二个参数是保留的小数位
    7. //第三个参数是多余的数字如何进行处理
    8. //HALF_UP:四舍五入
    9. BigDecimal b3 = b1.divide(b2,3, RoundingMode.HALF_UP);
    10. System.out.println(b3);

    日期类

  2. 日期:年月日,时间:小时,分钟,秒

  3. Date类的基本使用

    1. //年,从1900年开始
    2. int year = date.getYear();
    3. //月 从0开始
    4. int month = date.getMonth();
    5. //日
    6. int d = date.getDate();
    7. //周几
    8. int day = date.getDay();
    9. //小时
    10. int hour = date.getHours();
    11. //分钟
    12. int minutes = date.getMinutes();
    13. //秒
    14. int second = date.getSeconds();
    15. System.out.println("当前时间:"+year+"年"+month+"月"+d+"日");
    16. System.out.println("当前时间:"+hour+":"+minutes+":"+d+"");
    17. System.out.println("当前时间:星期"+day);
  4. Calendar日历类

    1. //不需要new,直接通过静态方法获取Calendar的实例
    2. Calendar calendar = Calendar.getInstance();
    3. int year = calendar.get(Calendar.YEAR);
    4. int month = calendar.get(Calendar.MONTH);
    5. int date = calendar.get(Calendar.DATE);
    6. int hour = calendar.get(Calendar.HOUR_OF_DAY);
    7. int minutes=calendar.get(Calendar.MINUTE);
    8. int second =calendar.get(Calendar.SECOND);
    9. int day = calendar.get(Calendar.DAY_OF_WEEK);
    10. System.out.println("当前时间:"+year+"年"+(month+1)+"月"+date+"日");
    11. System.out.println("当前时间:"+hour+":"+minutes+":"+second+"");
    12. System.out.println("当前时间:星期"+(day-1));
  5. 时间的计算

    1. 无论是Date类,还是Calendar都不能直接进行计算
    2. 提出一个时间戳的概念可以用于计算
  6. 时间戳是一个整数(long),它的最小单位是毫秒,记录了1970开始到现在的每一毫秒

    1. long now = calendar.getTime().getTime();//获取当前的时间戳
    2. long time = now+ 1000*60*60*20; //计算新的时间
    3. calendar.setTimeInMillis(time);//设置新的时间
  7. 可以使用Calendar设置新的时间,并获取新的时间戳,用于和当前时间进行计算获取倒计时

    1. Calendar calendar = Calendar.getInstance();
    2. long now = calendar.getTime().getTime();
    3. //设置当个属性
    4. calendar.set(Calendar.HOUR_OF_DAY,17);
    5. calendar.set(Calendar.MINUTE,50);
    6. //获取当前5.50的时间戳
    7. long time = calendar.getTime().getTime();
    8. long left = time - now;
    9. System.out.println(left/1000/60);
  8. 实际开发的时候,用户输入的数据都是字符串,那么字符串要转换成日期类型,就需要使用SimpleDateFormat类进行格式化

    1. 可以将字符串转换日期

      1. SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd");
      2. Date birthdate = sdf.parse(birthday);
      3. long now = new Date().getTime();
      4. System.out.println((now-birthdate.getTime())/1000/60/60/24/365);
    2. 也可以将日期转成字符串

      1. System.out.println(new Date());
      2. //定义格式
      3. SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
      4. //进行转换
      5. String str = sdf.format(new Date());
      6. System.out.println(str);
    3. 常用时间模式字母: | 字母 | 日期或时间 | 示例 | | —- | —- | —- | | y | 年 | 2019 | | M | 年中月份 | 08 | | d | 月中天数 | 10 | | H | 1天中小时数(0-23) | 22 | | m | 分钟 | 16 | | s | 秒 | 59 | | S | 毫秒 | 367 |

System系统类

  1. System.out.println输出
  2. System.arraycopy(…): 数组拷贝
  3. System.currentTimeMillis(): 获取当前的系统时间
  4. System.gc():呼叫垃圾回收期进行内存的释放
  5. System.exit(0);退出当前程序