一、内部类
- 理解:一个类中再声明另外一个类
- 分类:
- 成员内部类
- 静态内部类
- 接口内部类
- 局部内部类
- 匿名内部类
1.成员内部类
案例:创建成员内部类的对象,并调用方法
javaimport com.lv.outter_inner_02.Outter.Inner; public class Test { public static void main(String[] args) { Inner inner = new Outter().new Inner(); inner.method(); } } package com.lv.outter_inner_02; //外部类 public class Outter { private String str1 = "属性1"; String str2 = "属性2"; protected String str3 = "属性3"; public String str4 = "属性4"; final String str5 = "属性5"; static String str6 = "属性6"; static final String str7 = "属性7"; //成员内部类 class Inner{ String str1 = "成员内部类的属性"; public void method(){ System.out.println("成员内部类的方法"); System.out.println(str1);//this.str1 System.out.println(Outter.this.str1); System.out.println(str2);//Outter.this.str2 System.out.println(str3);//Outter.this.str3 System.out.println(str4);//Outter.this.str4 System.out.println(str5);//Outter.this.str5 System.out.println(str6);//Outter.str6 System.out.println(str7);//Outter.str7 } } }
总结:
- 创建成员内部类对象,必须先创建外部类对象,再创建内部类对象
- 成员内部类可以使用外部类所有的属性
2.静态内部类
案例:创建静态内部类的对象,并调用方法
javaimport com.lv.outter_inner_03.Outter.Inner; public class Test { public static void main(String[] args) { Inner inner = new Outter.Inner(); inner.method(); } } package com.lv.outter_inner_03; //外部类 public class Outter { static String str1 = "属性1"; static final String str2 = "属性2"; //静态内部类 static class Inner{ public void method(){ System.out.println("静态内部类的方法"); System.out.println(str1);//Outter.str6 System.out.println(str2);//Outter.str7 } } }
总结:
- 创建静态内部类对象,不用创建外部类对象
- 静态内部类只能使用外部类的静态属性
3.接口内部类
案例:创建接口内部类的对象,并调用方法
javaimport com.lv.outter_inner_04.Outter.Inner; public class Test { public static void main(String[] args) { Inner inner = new Outter.Inner(); inner.method(); } } package com.lv.outter_inner_04; //外部接口 public interface Outter { //接口内部类 //public static class Inner{ class Inner{ public void method(){ System.out.println("接口内部类的方法"); } } }
总结:
- 接口内部类就是静态内部类
- 接口内部类默认使用public static修饰
4.局部内部类
案例:调用局部内部类里的方法
javapackage com.lv.outter_inner_05; public class Test { public static void main(String[] args) { Outter outter = new Outter(); outter.method(); } } package com.lv.outter_inner_05; //外部类 public class Outter { public void method(){ //局部内部类 class Inner{ public void innerMethod(){ System.out.println("局部内部类里的方法"); } } //创建局部内部类对象 Inner inner = new Inner(); inner.innerMethod(); } }
总结:
- 局部内部类不能使用访问修饰符修饰(因为局部内部类的作用域就是在该方法内)
- 局部内部类使用到外部类方法里的变量,该变量在JDK1.8开始会自动变成常量
5.匿名内部类
案例:
javapackage com.lv.outter_inner_08; public class Test { public static void main(String[] args) { A a = new A() { @Override public void method() { // TODO Auto-generated method stub } }; a.method(); } } package com.lv.outter_inner_08; public abstract class A { public abstract void method(); }
实现过程:
- 底层创建匿名类(Test01$1.class),继承A类,重写method()
- 创建匿名类对象
- 将匿名类对象的内存地址赋值给父类的引用 -- 多态
6.应用场景
- A类的对象只在B类中使用,并且A类使用到B类所有的属性 -- 可以将A类设置为B类的成员内部类(常用)
- A类的对象只在B类中使用,并且A类使用到B类的静态属性 -- 可以将A类设置为B类的静态内部类(常用)
- A类(抽象类)的子类只创建一次对象,就没必要去创建子类 -- 直接使用匿名内部类(new A())(常用)
- I1接口的实现类只创建一次对象,就没必要去创建实现类 -- 直接使用匿名内部类(new I1())(常用)
- A类的对象只在B类某个方法中使用 -- 可以将A类设置为B类的局部内部类(少见)
- A类的对象只在I1接口中使用 -- 可以将A类设置为I1接口的接口内部类(少见)
二、常用类
1.包装类/封装类
理解:包装类是8种基本数据类型对应的类
出现原因:
Java为纯面向对象语言(万物皆对象),但是8种基本数据类型不能new对象,破坏了Java为纯面向对象语言的特征,所以Java又为8种基本你数据类型分别匹配了对应的类,这种类叫做包装类/封装类
基本数据类型与引用数据类型继承关系
基本数据类型 引用数据类型 继承关系 byte Byte Object.Number.Byte short Short Object.Number.Short int Integer Object.Number.Integer long Long Object.Number.Long float Float Object.Number.Float double Double Object.Number.Double char Character Object.Character boolean Boolean Object.Boolean 注意:int类型对应的包装类是Integer,char类型对应的包装类是Character
以int为例:
javapackage com.lv.package_class; public class Test { public static void main(String[] args) { //手动装箱:基本数据类型 转 引用数据类型 int a = 100; Integer integer = Integer.valueOf(a); System.out.println(integer); //手动拆箱:引用数据类型 转 基本数据类型 Integer integer = new Integer(100); int b = integer.intValue(); System.out.println(b); //JDK1.5开始提供自动装箱和自动拆箱的特性 //自动装箱:基本数据类型 转 引用数据类型 int c = 100; Integer integer = c;//底层实现:Integer.valueOf(i); System.out.println(integer); //自动拆箱:引用数据类型 转 基本数据类型 Integer integer = new Integer(100); int d = integer;//底层实现:integer.intValue(); System.out.println(d); //将字符串转换为int String str = "123"; int e = Integer.parseInt(str); System.out.println(e); } }
经验:学习包装类要举一反三
包装类(int为例)深入
javapackage com.lv.package_class; public class Test { public static void main(String[] args) { Integer integer1 = Integer.valueOf(100); Integer integer2 = Integer.valueOf(100); System.out.println(integer1 == integer2);//true Integer integer3 = Integer.valueOf(200); Integer integer4 = Integer.valueOf(200); System.out.println(integer3 == integer4);//false } }
底层分析:
在Integer中,有一个Integer的缓存类
javapublic static Integer valueOf(int i){ if(i>=IntegerCache.low && i<=IntegerCache.higt){ return IntegerCache.cache[i-IntegerCache.low]; } return new Integer(i); } private static class IntegerCache{ static final int low = -128; static final int higt = 127; static final Integer[] cache; static{ cache = new Integer[higt - low + 1]; int j = low; for (int i = 0; i < cache.length; i++) { cache[i] = new Integer(j++); } } }
当输入值超出范围时就会return新的Integer对象,
这就是为什么System.out.println(integer3 == integer4);的结果为false。
2.字符串的类
- 分类:
- String
- StringBuffer
- StringBuilder
1.String的使用
- 案例以及分析:
javapackage com.lv.string_class; public class Test { public static void main(String[] args) { String str = "123abc"; str = str.concat("DEF123");//concat在末尾追加,并返回新的字符串 str = str.substring(2);//substring从开始下标处截取到字符串末尾,并返回新的字符串 str = str.substring(1, 7);//substring从开始下标处(包含)截取到结束下标处(排他),并返回新的字符串 str = str.toLowerCase();//toLowerCase转小写,并返回新的字符串 str = str.toUpperCase();//toUpperCase()转大写,并返回新的字符串 //-------------------------------------------------------------------- str = " 123 abc DEF 123 "; str = str.trim();//trim去除首尾空格,并返回新的字符串 str = str.replace('2', '6');//replace替换字符,并返回新的字符串 str = str.replaceAll("163", "666888");//replaceAll替换字符串,并返回新的字符串 str = str.replaceFirst("666", "7777");//replaceFirst替换第一次出现的字符串,并返回新的字符串 str = str.replaceAll(" ", "");//去除空格(将空格字符串替换成空内容的字符串) System.out.println("判断两个字符串是否相同(区分大小写):" + str.equals("7777888abcDEF666888")); System.out.println("判断两个字符串是否相同(不区分大小写):" + str.equalsIgnoreCase("7777888ABCDEF666888")); System.out.println("判断字符串是否以某个字符串开头:" + str.startsWith("777")); System.out.println("判断字符串是否以某个字符串结尾:" + str.endsWith("666888")); System.out.println("查询出子字符串在字符串中第一次出现的下标:" + str.indexOf("88")); System.out.println("查询出子字符串在字符串中最后一次出现的下标:" + str.lastIndexOf("88")); System.out.println("获取指定下标上的字符:" + str.charAt(7)); System.out.println(str);//7777888abcDEF666888 //------------------------------------------------------------- //将其他类型转型成String System.out.println(String.valueOf(100));//int -> String System.out.println(String.valueOf(123.123));//double -> String System.out.println(String.valueOf('a'));//char -> String System.out.println(String.valueOf(true));//boolean -> String System.out.println(String.valueOf(new char[]{'a','b','c'}));//char[] -> String //将其他类型转型成String -- 简化版 System.out.println(100 + ""); System.out.println(123.123 + ""); System.out.println('a' + ""); System.out.println(true + ""); } }
深入------String拼接创建对象
案例以及分析:
javapackage com.lv.string_class; public class Test { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; System.out.println(str1 == str2);//true //两个常量在编译时直接拼接 String str3 = "ab" + "c"; System.out.println(str1 == str3);//true //两个常量在编译时直接拼接 final String s1 = "ab"; final String s2 = "c"; String str4 = s1 + s2;//反编译:String str4 = "abc"; System.out.println(str1 == str4);//true //变量拼接,底层会创建StringBuilder对象 String s3 = "ab"; String s4 = "c"; String str5 = s3 + s4;//底层实现:String str5 = (new StringBuilder(String.valueOf(s3))).append(s4).toString() System.out.println(str1 == str5);//false } }
2.StringBuffer的使用
案例以及分析:
javapackage com.lv.string_class; public class Test02 { public static void main(String[] args) { //创建StringBuffer对象 StringBuffer sb = new StringBuffer(); //在末尾追加字符串 sb.append("123abc"); sb.append("DEF123"); sb.insert(6, "XXYYZZ");//将字符串插入到指定下标的位置 sb.setCharAt(6, 'x');//替换指定下标上的字符 sb.replace(3, 6, "aabbcc");//替换开始下标处(包含)到结束下标处(排他)的字符串 sb.deleteCharAt(3);//删除指定下标上的字符 sb.delete(3, 17);//删除开始下标处(包含)到结束下标处(排他)的字符串 sb.reverse();//反转字符串 System.out.println(sb.toString());//321321 } }
StringBuffer的深入 -- StringBuffer底层源码
见StringBuffer底层源码分析
3.StringBuilder的使用
理解
- StringBuilder代表可变的字符序列。
- StringBuilder称为字符串缓冲区
工作原理:
- 预先申请一块内存,存放字符序列,如果字符序列满了,会重新改变缓存区的大小,以容纳更多的字符序列。
StringBuilder是可变对象,这个是String最大的不同深入:现在的需求要存储10000个长度的数据,不要使用new StringBuilder()的方式,因为使用无参构造,底层会创建16长度的容器,存储10000个数据需要多次扩容,效率极低,直接使用new StringBuilder(10000)的方式,一步到位。
4.StringBuffer和StringBuilder区别
- StringBuffer和StringBuilder在使用层面上是一模一样的(调用方法)
- StringBuffer和StringBuilder都是继承的同一个父类(AbstractStringBuilder),而且底层实现都是依赖于父类
- 唯一不同就是StringBuffer方法上加了锁(synchronized),就意味着StringBuffer是线程安全的
应用场景:
- 单线程的程序:使用StringBuilder,因为不上锁
- 多线程的程序:使用StringBuffer,上锁是因为不让其他线程抢到资源
注意:StringBuilder的效率比StringBuffer高,因为没有上锁和解锁的过程
5.频繁的拼接字符串使用StringBuilder或StringBuffer
当频繁使用拼接字符串时:
String的底层实现:
str = str + "11223";
str = new StringBuidler(String.valueOf(str)).append("11223").toString();
这样会导致内存占用过高。