黑马程序员——Java语言基础(下)

来源:互联网 发布:wamp php.ini在哪 编辑:程序博客网 时间:2024/06/05 22:54

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

内容提要:

        模板设计模式
        接口
        面向对象特征三之多态
        两个常用类:Object类和Class类
        内部类
        静态内部类
        匿名内部类
        包package
        import关键字
        String、StringBuffer、StringBuilder类


模板设计模式

        在定义功能时,功能的一部分是确定的,而确定的部分在使用不确定的部分,那么这时就将不确定的部分进行封装和暴露,以此让子类实现。

        应用:计算某段程序的运行时间,此时待测试的部分程序是模糊的,可以将该部分暴露出去。

/* * 用于获取代码运行时间 * */abstract class GetTime {public final void getTime() {long start = System.currentTimeMillis();runcode();long end = System.currentTimeMillis();System.out.println("毫秒:" + (end - start));}public abstract void runcode(); // 将不确定的部分进行抽象化}class SubTime extends GetTime {public void runcode() // 让子类去实现具体的工作{for (int x = 0; x < 4000; x++)System.out.println(x);}}public class Test {public static void main(String[] args) {new SubTime().getTime();// System.out.println("hello world");}}

       模板设计模式通常使用抽象类中,但并不一定只能是这样的。

接口

        初期理解,可以认为是一个特殊的抽象类,也就是说当抽象类中的方法都是抽象的,那么该类可以通过接口的形式表现出来。但是接口类中还可以定义其他字段,比如定义变量等等。

        接口常用于定义常量和抽象方法,定义的时候有相同的修饰符;定义常量时的修饰符是:public static final;定义方法时的修饰符是:public abstract。注意:接口中的成员都是public的。

public class InterfaceDemo {public static void main(String[] args) {// TODO Auto-generated method stubTest t = new Test();System.out.println(t.NUM);System.out.println(Inter.NUM);System.out.println(Test.NUM);t.show();}}interface Inter {public static final int NUM = 3;public abstract void show();}class Test implements Inter {public void show() {System.out.println("<----Test class show()--->");}}

        接口和抽象类的区别:使用接口(接口和类之间的关系)在定义其他类时,其他类称为是实现类,使用关键字implments。而接口与接口之间是继承关系。此外,接口之间可以存在多继承,但在类与类之间是不可以的。在抽象类和抽象类之间(最后还是落实到类上),使用继承来说明,因为父类中可以有非抽象的属性可以直接继承到子类使用。

        特别的:接口可以多继承;类与类之间不能实现多继承。

        “写内容,那是需要实现(implements)的;继承是不需要写具体的内容的,只需要从父类中拿即可。”

        在接口(全都是抽象的)的子类中只能全部覆盖了全部方法,才能进行实例化,否则子类也仍然是一个抽象类。

        接口是不可以创建对象的,因为有抽象方法。

        接口可以被类多实现,也就是说一个类可以实现多个接口。

        此外,在定义一个类,继承另一个类的同时实现多个接口。

public class InterfaceDemo {public static void main(String[] args) {// TODO Auto-generated method stubTest t = new Test();System.out.println(t.NUM);System.out.println(InterA.NUM);System.out.println(Test.NUM);t.show();}}interface InterA {public static final int NUM = 3;public abstract void show();}interface InterB {void show(); //默认的修饰方式:public abstract}class Demo{public void function(){}}class Test extends Demo implements InterA, InterB { //Test类在继承Demo了的同时,有类的多实现(接口)public void show(){System.out.println("<----Test class show---->");}}

        接口是对外暴露的规则;

        接口是程序的功能扩展(扩展功能,“like”);继承是“所属于的”(“is”),某某是某某的一种。

        接口类在编译后也是以.class文件的形式存在的。

面向对象特征三之多态

        对象的多态,类似于:动物中有猫和狗两个类,两种定义实体的方式:猫 x = new 猫(); 动物 x = new 猫();。当类与类之间发生了关系后,实体(new出来的对象)还可以具备其他类型(比如,父类型),这是对象的多态性。

        “多态,是某一类事物的多种表现形态。”

        多态的表现形式:父类的引用指向子类的对象,潜在地表示一个对象有多个类型,即父类的引用也可以接受自己的子类对象。

public class DuoTaiDemo {public static void main(String[] args) {// TODO Auto-generated method stubCat c = new Cat();c.eat();Dog d = new Dog();d.eat();Animal a1 = new Cat(); // 多态的表现形式a1.eat();Animal a2 = new Dog(); // 多态的表现形式a2.eat();}}abstract class Animal {public abstract void eat();}class Cat extends Animal {public void eat() {System.out.println("吃鱼");}public void catchMouse() {System.out.println("抓老鼠");}}class Dog extends Animal {public void eat() {System.out.println("吃骨头");}public void watch() {System.out.println("看家");}}

        重载和覆盖就是函数的多态性体现。

        Java引用变量有两个类型:一个是编译时类型,一个是运行时类型。

        编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态。表现形式:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征。但引用变量只能调用它编译时类型的方法,而不能调用它允许时类型的方法,即使它实际所引用的对象确实包含该方法。

        多态前提:必须是类与类之间有关系,要么是继承要么是实现。通常还有一个前提:存在覆盖方法,否则就没有任何意义。前期预先调用功能,后期定义子类去实现功能并把子类作为参数传递进去,这就实现了扩展性能。

        多态的好处:多态的出现大大提高了程序的扩展性。

        多态的弊端:只能使用父类的引用访问父类中的成员,不能预先使用子类。在编译期,JVM会检查父类中是否存在该方法,假若不存在则编译不通过。而在运行期,则调用的是子类的方法,其中假若子类没有该方法,则调用父类的方法。这就是为什么还要存在覆盖的原因,因为此时子类还不存在。

        多态的应用:Animal a = new Cat();该语句存在类型的提升,以前学到的类型自动提升就引用到了面向对象中,称为:向上转型或转成父类型。这种转型只是表明这个引用变量的编译时类型时父类,但实际执行它的方法时,依然表现出子类对象的行为方式。如果想要调用子类的特有方法,这个时候强制将父类引用转成子类类型:Cat c = (Cat) a;,这个时候就可以调用子类对象的特有方法,称为向下转型。

        千万不要出现这样的操作:将父类对象转换成子类类型。我们能够转换的是父类的引用指向了自己的子类对象,该引用既可以被提升也可以被转换,即多态自始至终都是子类的对象在做着变化

        关键字instanceof:用于判断引用指向的对象或实体是否属于某个类型的。instanceof运算符前面操作数的编译时类型要么与后面的类型相同,要么与后面的类具有父子继承关系,否则会引起编译错误。

        多态将对象调用变得简单了,以前是一个对象调用一个方法,现在是一个对象指挥一批方法。

        多态中成员函数的特点:在编译时期,参阅引用型变量所属的类(父类)中是否有调用的方法;编译时期检查的是语法错误,此时虚拟机还没有运行且没有产生对象。如果有,编译通过,如果没有编译失败;在运行时期,参阅对象所属的类中是否有调用的方法(有,则运行自己的;无,则运行父类的);在运行期间,内存中执行代码的是内存中产生的对象。

        简单总结:非静态成员函数(注释部分:非静态成员函数有“覆盖”特性。)在多态调用时,编译看左边,运行看右边;静态成员函数的特点:无论编译和运行,都参考左边。

        多态中成员变量的特点:无论编译和运行,都参考左边(引用变量所属的类)。

        接口引用也可以指向自己的子类对象,这也是一种多态的表现形式。

        多态,降低了具体链接之间的耦合性。

两个常用类:Object类和Class类

        Object类:是所有对象的直接或者间接父类,就是传说中的“上帝”。

        该类中定义的肯定是所有对象具备的功能。

        Object类中已经提供了对对象是否相同的比较方法,如果自定义类中也有比较相同的功能,没有必要重新定义,只要沿袭父类中的功能,建立自己特有的比较内容即可,即方法的覆盖。

        Class类:比如A.class和B.class,这些class文件都有名称,这些文件内部都有构造函数和一般方法。

        Class类用于描述这些class文件,这部分知识可用于类的反射。

内部类

        将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)。

public class InnerClassDemo {public static void main(String[] args) {// TODO Auto-generated method stubOuter out = new Outer();out.method();// 直接访问内部类中的成员,需要先新建外部类对象Outer.Inner in = new Outer().new Inner();in.function();}}class Outer {private int x = 3;class Inner { // 内部类极有可能被private修饰进行封装,在成员位置上时int x = 4;void function() {int x = 6;System.out.println("inner:" + Outer.this.x); // 内部类中可以访问外部类的私有成员}}void method() {Inner in = new Inner(); // 在内部类的外部定义内部类对象in.function();}}

        “类内部”包括类中的任何位置,甚至在方法中也可以定义内部类。

        方法里定义的内部类被称为局部内部类。

        成员内部类时一种与成员变量、方法、构造器和初始化块相似的类成员。局部内部类和匿名内部类则不是类成员。

        内部类的访问规则:内部类可以直接访问外部类中的成员,包括私有的(原因:内部类中持有了一个外部类的引用,该引用的写法:Outer.this);外部类要访问内部类,必须建立内部类对象。

        直接访问内部类:Outer.Inner inner = new Outer().new Inner();内部类作为外部类的一个成员,可以被成员修饰符所修饰,比如private:将内部类在外部类中进行封装。一般内部类是不会被公有修饰的,除非是接口。

        包含内部类的类也被称为外部类(有的地方也叫宿主类)。

        内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。

        内部类成员可以直接访问外部类的私有数据,但外部类不能访问内部类的实现细节。匿名内部类适合用于创建那些仅需要一次使用的类。

静态内部类

        静态内部类:内部类可以被static修饰,此时内部类就具有静态特性。此时,只能直接访问外部类的static成员,出现了访问局限,和之前的Outer.this引用对比。

        在外部其他类中访问静态内部类的非静态方法:new Outer.Inner().function();,其中Outer.Inner实际作为一个类名而存在。

        在外部其他类中访问静态内部类的静态方法:Outer.Inner.function();。

public class InnerClassDemo {public static void main(String[] args) {// TODO Auto-generated method stubOuter out = new Outer();out.method();//在外部类的外面创建内部类对象,Outer类加载时,其中的Inner即加载了,不需要对象new Outer.Inner().function1(); //将Outer.Inner作为整体,使用内部类对象调用方法Outer.Inner.function2(); //访问内部类的静态方法}}class Outer {private static int x = 3;static class Inner { //静态内部类void function1() {System.out.println("function1-->inner:"+x); //可以访问外部类的静态成员变量 }static void function2(){System.out.println("function2-->inner:"+x); //可以访问外部类的静态成员变量 }}void method() {Inner in = new Inner(); //在内部类的外部定义内部类对象in.function1();}}

        注意:当内部类中定义了静态成员时,该内部类必须是静态的。

        当外部类中的静态方法访问内部类时,内部类也必须是static的。

        内部类定义在局部时:不可以被成员修饰符修饰;可以直接访问外部类的成员,因为还持有外部类的引用;但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。

        为什么要定义内部类:当描述事物时,事物的内部还有事物,该事物用内部类来描述。因为内部事物的描述需要外部事物的内容。比如:人体类和心脏类,心脏需要访问人体内部器官;此时还可以将心脏私有化,用private修饰。
内部类定义在局部时,不可以被成员修饰符修饰,可以直接访问外部内中的成员,因为还持有外部类中的引用,但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量(java 8中可以访问)。

匿名内部类

        匿名内部类就是内部类的简写格式,也就是说没有名字的内部类。

        定义匿名内部类的前提:内部类必须继承一个类,或者实现接口。

        匿名内部类的格式:new 父类或接口(){定义子类的内容}。

        其实匿名内部类就是一个匿名子类对象,是把定义类和建立对象封装为一体的表现形式,而且这个对象有点“胖”,可以理解为“带有内容的对象”。

        匿名对象,就是为了简化书写并覆盖方法。匿名内部类中定义的方法最好不要超过3个,假若超过了,就定义一个类继承超类即可。

public class InnerClassDemo {public static void main(String[] args) {// TODO Auto-generated method stubOuterClass o = new OuterClass();o.function();}}abstract class AbsDemo {abstract void show();}class OuterClass {int x = 3;/* * 需要将该内部类改写为匿名的形式 class Inner extends AbsDemo{ //内部类继承了另外一个类 void show() { * System.out.println("x = " + x); } } */public void function() {// new Inner().show();new AbsDemo() {void show() // 抽象方法的实现{System.out.println("x = " + x);}}.show(); // 匿名子类创建对象,并调用内部方法}}

包package

        包package的概念:定义包使用关键字package(类似于windows系统下的文件夹特性)。特点:用来解决类的命名冲突、类文件管理等问题;为类提供多层命名空间(注释部分:Java允许将一组功能相关的类放在同一个package下,从而组成逻辑上的类库单元。);必须写在源程序文件的第一行;类名的全称是:包名.类名;包也是一种封装形式。书写包名,是为了提供包所在位置(即文件路径)。如果希望把一个类放在指定的包结构下,应该在Java源程序的第一个非注释行放置如下格式的代码:package mypack;表示该源文件被编译后,.class文件将存放在mypack文件夹中。编译时,一定要使用javac –d . DemoA.java (注释部分:假若不使用javac –d ,编译器不会为Java源文件生成相应的文件结构。)表示编译文件夹将存放在当前目录下,编译指令指出了绝对路径(自动生成文件夹目录)。一旦在Java源文件中使用上述的语句,就意味着该源文件里定义的所有类都属于这个包。位于包中的每个类的完整类名都应该是包名和类名的组合。

        Java的包机制需要有两个方面保证:源文件里使用package语句指定包名;class文件必须放在对应的路径下。
为了更好地组织系统中类库,建议定义包名不要重复,可以使用url来完成定义,因为url是唯一的。比如:www. itcast. cn,对应使用的包名:package cn.itcast.demo;package cn.itcast.test等。

        如果需要使用不同包中的其他类时,总是需要使用该类的全名。

        不同包中的类,该如何访问?错误一:类名写错,类名的全名称应是:包名.类名(建立一个对象,需要使用类名的全称);错误二:类包不在当前目录下,需要设置classpath,告诉jvm到什么地方寻找该类;错误三:有了包,范围变大(需要考虑类在包中的权限),一个包中的类要被访问,必须要有足够大的权限,所以被访问的类要被public修饰。类公有后,被访问的成员也要公有,才可以被访问。

        总结:包与包之间进行访问,被访问包中的类以及类中的成员,需要public修饰;不同包中的子类还可以访问父类中被protected权限修饰的成员;

        包与包之间可以使用的权限只有两种,public和protected(只能给子类使用)。

        类的访问修饰符只有两种:默认权限和public。类名前面加了public访问权限后,文件名必须和类名保持一致(注释部分:如果Java程序源代码里定义了一个public类,则该源文件的主文件名必须与该public类的类名相同。因此,一个Java源文件里只能定义一个public类。)。两个java文件中不能出现相同名字的类或接口。

import关键字

        import关键字:为了简化类名全称的书写,使用import关键字(导入的是包中的类),比如:import packb.DemoC。导入后,在新建对象时只需要写类名即可。import lee.*;表明导入lee包下的所有类,但其下的子包内的类不会被导入。建议,不要写通配符*,需要用到包中的那个类,就导入哪个类。

        一个Java源文件只能包含一个package语句,但可以包含多个import语句,多个import语句用于导入多个包层次下的类。

        Java默认为所有源文件导入java.lang包下的所有类,因此前面在Java程序中使用的String、System类都无须使用import语句来导入这些类。

String、StringBuffer、StringBuilder类

        Java API部分中的String类:使用的是java.lang包中的String类。

        字符串最大的特点在于:一旦被初始化,就不可以被改变。”abc”是一个字符串类型的对象,也是一个常量。String类复写了Object类中的equals方法,该方法用于判断字符串是否相同。

public class Test {public static void main(String[] args) {// TODO Auto-generated method stubString str1 = "abc";System.out.println(str1.hashCode()); //输出哈希值96354str1 = "kk";System.out.println(str1.hashCode()); //输出哈希值:3424/*实验说明:并不是字符串内容改变了,而是引用改变了*/String str2 = "abc";String str3 = new String("abc");System.out.println(str2 == str3); //falseSystem.out.println(str2.equals(str3)); //trueSystem.out.println(str2+"def"); //abcdef/*实验说明:以上两种创建字符串的方式略有不同*/}}
        String s1 = “abc”;String s2 = new String(“abc”)的区别:对于第一个表达式,如果“常量池”中已经有了"abc"对象,则直接使用该对象的引用,如果没有则将在常量池中开辟内存空间,并存储该值(内存中只有一个对象);后者先在常量池中创建一个abc对象,并将其复制到堆内存中(内存中有两个对象)。

        常量池指的是在编译器被确定,并被保存在已编译的.class文件中的一些数据,其中包括关于类、方法、接口中的常量,也包括字符串直接量。常量池存在于数据共享区中。"abc"常量字符串会直接在常量池中开辟空间,为了节约内存,之后建立的字符串对象,只要内容是abc,就直接指向常量池的地址。

        String类适用于描述字符串事物,它提供了多个方法对字符串进行操作,长度固定。

        常见的操作有:

        获取:字符串中包含的字符数:length(),根据位置获取位置上某个字符:charAt(),根据字符获取该字符在数组中的位置:indexOf()、lastIndexOf();其中以上方法均有重载方法。

        判断:字符串中是否包含某一子串:contains(),字符串是否有内容:isEmpty(),字符串是否是以指定内容开头:startsWith(),字符串是否是以指定内容结尾:endsWith()。

        转换:将字符数组转成字符串:String类的构造函数即可实现;String类静态方法:copyValueOf()、valueOf(),将字符串转成字符数组:toCharArray(),将字节(byte[ ])数组转成字符串:String类的构造函数即可实现,将字符串转成字节数组:getBytes(),将基本数据类型转成字符串:String类静态方法:valueOf()。字符串和字节数组在装换过程中,是可以指定编码表的;

        替换:用新的字符或字符串替换原字符串中的内容:replace()。

        切割:函数返回的是字符数组:split()。

        子串:获取字符串的一部分:substring()。

        将字符串转成大写或者小写:toUpperCase()、toLowerCase()

        将字符串两端的多个空格去除:trim()

        对两个字符串进行自然顺序的比较:compareTo()

public class Test {public static void main(String[] args) {method_7();}public static void method_1() {String str1 = "abczhanenbe";sop(str1.length()); // 输出字符串长度sop(str1.charAt(2)); // 获取脚标为2的字符sop(str1.indexOf('a', 3)); // 从脚标为3开始往后寻找字符asop(str1.lastIndexOf('b')); // 反向查找,输出脚标值}public static void method_2() {String str = "ArrayDemo.java";sop(str.startsWith("Array")); // 判断字符串是否以Array开头sop(str.endsWith(".java")); // 判断字符串是否以.java结尾sop(str.contains("Demo")); // 判断字符串是否包含Demo字符串sop(str.indexOf("Demo")); // 获取脚标位置sop(str.equals("Arraydemo.java")); // 判断字符串内容是否相等sop(str.equalsIgnoreCase("Arraydemo.java")); // 忽略大小写}public static void method_3() {char[] chars = { 'q', 'a', 'w', 'e', 'v', 'd' };String s1 = new String(chars);String s2 = new String(chars, 1, 3); // 取出部分字符构成字符串sop("s1=" + s1);sop("s2=" + s2);String str = "zhangfeng";char[] chs = str.toCharArray(); // 将字符串转化为字符数组for (int index = 0; index < chs.length; index++) // 遍历字符数组{sop(chs[index]);}}public static void method_4() {String s = "hello";String s1 = s.replace('l', 't');String s2 = s.replace("lo", "aa");sop(s);sop(s1); // 内存中有两个字符串,s1指向的是新字符串sop(s2);}public static void method_5() {String s = "zhangsan,lisi,wangwu";String[] arrs = s.split(",");for (int index = 0; index < arrs.length; index++)sop(arrs[index]);}public static void method_6() {String s = "zhangsan";sop(s.substring(2)); // 从指定位置开始到结尾sop(s.substring(2, 4)); // 包含头位置,不包含尾位置}public static void method_7() {String s = " zHbng Sbn ";sop(s.toUpperCase()); // 转换成全大写sop(s.toLowerCase()); // 转化为全小写sop(s.trim());sop(s.compareTo(" zHa"));}public static void sop(Object obj) {System.out.println(obj);}}

结果描述:需要指出的是trim()方法只能去除字符两端的空格符。

        算法模拟:

        1.模拟一个trim方法,去除字符串两端的空格;

public static String myTrim(String str) //去掉字符两端的空格{int start = 0, end = str.length()-1;while(start<=end && str.charAt(start)==' ') //获取某脚标位置的字符start++;while(start<=end && str.charAt(end)==' ')end--;return str.substring(start,end+1); //得到子字符串}

        2.将字符串反转,扩展部分:将一个字符串中的指定部分进行反转;

/*思路: * 1.将字符串变成字符数组; * 2.对数组反转(这个相对简单) * 3.将字符数组变成字符串 * 4.扩展部分:将字符串的部分进行反转 * */public static String reverseString(String str){return reverseString(str, 0, str.length()); //取的是所有的字符串内容进行反转}public static String reverseString(String str, int start, int end){char[] chs = str.toCharArray(); //将字符串转化为字符数组reverse(chs, start, end); //反转数组内容return new String(chs); //返回转化后的字符串}private static void reverse(char[] arrs, int x, int y){for(int start = x, end = y-1; start < end; start++, end--) //包含头部,不包含尾部{swap(arrs, start, end);}}private static void swap(char[] arrs, int x, int y) //还可以使用异或实现{char tmp = arrs[x];arrs[x] = arrs[y];arrs[y] = tmp;}

        3.获取一个字符串在另一个字符串中出现的次数;

/* *思路: *1.定义计数器 *2.获取kk第一次出现的位置 *3.从第一次出现位置后剩余的字符串中继续获取kk出现的位置 *4.当获取不到时,计数结束 * */public class Test {public static void main(String[] args) {String str = "abkkcdkkefkks";sop("count=" + str.split("kk").length); // 和split()方法相比较:4sop("count=" + getSubCount_1(str, "kk")); // 3sop("count=" + getSubCount_2(str, "kk")); // 3}public static int getSubCount_1(String str, String key) {int count = 0;int index = 0;while ((index = str.indexOf(key)) != -1) // 获取字符串中的指定子串,并返回脚标{// sop("str="+str);str = str.substring(index + key.length()); // 获取子串count++;}return count;}public static int getSubCount_2(String str, String key) {int count = 0;int index = 0;while ((index = str.indexOf(key, index)) != -1) // 从指定位置开始匹配字符串中的指定子串,并返回脚标{// sop("index="+index);index = index + key.length();count++;}return count;}public static void sop(String str) {System.out.println(str);}}

        4.获取两个字符串中最大的相同字串…这些练习题均可在online上找到。

/* * 思路 1.将短的那个字符串按照长度递减的方式获取到; 2.将每获取到的子串去长串中判断是否包含 3.如果包含,则找到 */public static String getMaxSubString(String s1, String s2) {/* * 思想: 将问题进行“有机”切分,分为一个个小目标; 对解决问题的方法进行整合; */String max = "", min = "";max = (s1.length() > s2.length()) ? s1 : s2; // 条件判断语句min = (s1 == max) ? s2 : s1;for (int x = 0; x < min.length(); x++) // 循环减少量{for (int y = 0, z = min.length() - x; z != min.length() + 1; y++, z++) {String tmp = min.substring(y, z); // substring是包含头部,不包含尾部字符if (max.contains(tmp)) // if(s1.indexOf(tmp)!=-1)判断s1中是否包含有子串return tmp;}}return "没找到";}

        5.对一个字符串中的字符进行自然排序

/* * 需求:对一个字符串中的字符进行自然排序 * 思路: * 1.将字符串装换为字符数组 * 2.对字符数组进行选择、冒泡排序 * 3.对排好序的字符数组转换为字符串 * */public class StringDemo1 {public static void main(String[] args) {// TODO Auto-generated method stubString str = "vshzkebah13sk4+AE+cs";sop(raturalSortString(str));sop(getLowerCaseString(raturalSortString(str)));}public static String raturalSortString(String s) {char[] arr = s.toCharArray();selectSortArray(arr);return new String(arr);}public static void selectSortArray(char[] arr) {for (int x = 0; x < arr.length - 1; x++) {for (int y = x + 1; y < arr.length; y++) {if (arr[x] > arr[y])swap(arr, x, y);}}}public static void swap(char[] arr, int x, int y) {arr[x] = (char) (arr[x] ^ arr[y]);arr[y] = (char) (arr[x] ^ arr[y]);arr[x] = (char) (arr[x] ^ arr[y]);}// 判断小写字母部分在有序字符串中的位置,并将第一个位置和最后一个位置以数组的形式返回private static int[] lowerCaseIndex(String s) {char[] arr = s.toCharArray();int x = 0;for (; arr[x] < 'a'; x++);int y = arr.length - 1;for (; arr[y] > 'z'; y--);int[] numbers = { x, y + 1 };return numbers;}public static String getLowerCaseString(String s) {int[] arr = lowerCaseIndex(s);return s.substring(arr[0], arr[1]);}public static void sop(String str) {System.out.println(str);}}

        StringBuffer类,是线程安全的可变字符序列(可对字符串进行修改的):StringBuffer是一个容器(字符串缓冲区),可以对字符串内容进行增删,可以直接操作多个数据类型,是长度可变的。StringBuffer类的很多方法和Strin类是相同的,但最终都需要通过toString方法变成字符串。

        该类的对象特点符合:“面盆”理论,即在面盆里面添加字符或字符串,均得到的是原先的“面盆”。同时也具备:方法调用链规则。

        发现的一个规则:StringBuffer类的底层实现是容器,具有脚标特性,且和String类不同的是:StringBuffer类中有insert()方法,能够实现在特定脚标位置插入字符串。“凡是具有脚标特性的类,均具有数组相类似的操作。”

        什么时候需要使用字符缓冲区?当数据类型不确定,数据个数不确定,且最终需要变成字符串时,使用StringBuffer是最方便的。

        时常将字符数组和StringBuffer相比较,两者有利有弊。

public class Test {public static void main(String[] args) {method_4();}public static void method_4() {StringBuffer sb = new StringBuffer("absdfsf");char[] chs = new char[4];sb.getChars(1, 4, chs, 1); // 将缓冲区的指定数据存储到字符数组的指定位置for (int x = 0; x < chs.length; x++)sop("" + chs[x]);}public static void method_3() {StringBuffer sb = new StringBuffer("absf");// sb.replace(1,4,"java"); //字符串的替换sb.setCharAt(2, 'm'); // 替换某一位字符sop(sb.toString());}public static void method_2() {StringBuffer sb = new StringBuffer("abwfsdecdsf");sb.delete(1, 3); // 通过脚标删除指定字符串,包含头但不包含尾部sb.deleteCharAt(2); // 删除脚标处的字符// sb.delete(0,sb.length()); //清空StringBuffer缓冲区sop(sb.toString());}public static void method_1() {StringBuffer sb = new StringBuffer();StringBuffer sb1 = sb.append(34); // 方法返回的是一个StringBuffer对象sop(sb.toString()); // 将StringBuffer对象转变成String对象sop(sb1.toString());sop("sb==sb1:" + (sb == sb1)); // true 表示两个对象指向的是同一个对象// 可以直接操作多种数据类型sb.append("abc").append("123"); // 方法调用琏,都是同一个对象sop(sb.toString());sb.insert(1, "qq"); // 在字符串的指定脚标位置插入字符串sop(sb.toString());}public static void sop(String str) {System.out.println(str);}}

        代码分析:比较String类和StringBuffer类方法的区别。

        StringBuilder类(是StringBuffer类的简易替换):在JDK1.5版本之后出现的,唯一的不同之处在于StringBuffer(需要判断:锁)是线程同步的,StringBuilder(不需要判断:锁)是线程不同步的。StringBuilder是线程不同步的,因此在效率上相对于StringBuffer要高,以后在开发中建议使用StringBuilder类。


0 0
原创粉丝点击