一、内部类
- 理解:一个类中再声明另外一个类
- 分类:
- 成员内部类
- 静态内部类
- 接口内部类
- 局部内部类
- 匿名内部类
1.成员内部类
案例:创建成员内部类的对象,并调用方法
import 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.str1System.out.println(Outter.this.str1);System.out.println(str2);//Outter.this.str2System.out.println(str3);//Outter.this.str3System.out.println(str4);//Outter.this.str4System.out.println(str5);//Outter.this.str5System.out.println(str6);//Outter.str6System.out.println(str7);//Outter.str7}} }
总结:
- 创建成员内部类对象,必须先创建外部类对象,再创建内部类对象
- 成员内部类可以使用外部类所有的属性
2.静态内部类
案例:创建静态内部类的对象,并调用方法
import 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.str6System.out.println(str2);//Outter.str7}}}
总结:
- 创建静态内部类对象,不用创建外部类对象
- 静态内部类只能使用外部类的静态属性
3.接口内部类
案例:创建接口内部类的对象,并调用方法
import 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.局部内部类
案例:调用局部内部类里的方法
package 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.匿名内部类
案例:
package com.lv.outter_inner_08; public class Test {public static void main(String[] args) { A a = new A() { @Overridepublic 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为例:
package 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);//将字符串转换为intString str = "123";int e = Integer.parseInt(str);System.out.println(e);} }
经验:学习包装类要举一反三
包装类(int为例)深入
package 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);//trueInteger integer3 = Integer.valueOf(200);Integer integer4 = Integer.valueOf(200);System.out.println(integer3 == integer4);//false} }
底层分析:
在Integer中,有一个Integer的缓存类
public 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的使用
- 案例以及分析:
package 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//-------------------------------------------------------------//将其他类型转型成StringSystem.out.println(String.valueOf(100));//int -> StringSystem.out.println(String.valueOf(123.123));//double -> StringSystem.out.println(String.valueOf('a'));//char -> StringSystem.out.println(String.valueOf(true));//boolean -> StringSystem.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拼接创建对象
案例以及分析:
package 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的使用
案例以及分析:
package 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();这样会导致内存占用过高。