数据类型之变量
整数类型
Bit:在计算机中,数据的最小单位是位,位是指一位二进制数,英文名称是Bit。也称比特,1位信息只能表示2个状态(0或1)中的1个
Byte:字节,记为Byte或B,是计算机中信息的基本单位,表示8个二进制数位
所以1KB=1024Byte,1Byte=1024Bit
整数类型表数范围由bit位来决定,1byte=8位bit,1位bit可以表示0或者1,8位bit可以表示00000000到01111111(第一位是0表示正数,是1表示负数),也就是说1byte可以用8位bit表示就有2的8次方也就是256种可能,又因为Java规定,要从-128开始表示范围,所以byte在Java种可以表示-128到127这256位。
在Java中如果定义byte类型小于-128或者大于127会出错。
定义long类型时,变量后必须加"l"或者"L"
浮点型
定义float类型时,变量后必须加"f"或者"F",通常定义浮点型都是用double
字符型char(1字符=2字节)
char内部智能写一个字符,写多了会报错,char通常都有三种用法:1.声明一个字符。2.声明一个转义字符。3.使用Unicode值来表示字符型常量
布尔类型
布尔类型只表示true或者false,无关字节与字符
tips:如果想在字符串中使用双引号,可以用"代表
自动类型转换
小范围的变量类型转换为大范围的变量类型可以编译通过,反之不可以。
byte和char和short之间运算时,结果必须为int型
下图中的s2和b2如果用int来接的话就编译通过
强制类型转化
通过强制类型转换,可以把高精度的类型转换为低精度的类型,但是有可能损失精度
特殊编码情况
浮点类型操作时,默认类型是double类型
位运算符
数组
数组是在空间上是连续的,不像链表一样,通过指针连续,数组的长度是不可变的,当前的数组如果长度不够用了智能生成一个新的数组,把旧数组的数据放入新数组使用。
数组的用法
数组初始化:1.静态初始化(初始化和赋值同时进行)2.动态初始化(只初始化长度不赋值)
数组元素的默认初始值
当初始化一个数组并不给数组赋值时,数组元素的数据如下
二维数组的初始化
二维数组的存储结构
二维数组的长度与遍历
二维数组的内存结构
方法
局部变量与属性(全局变量)
局部变量不能定义修饰符,其修饰符等同于方法定义的修饰符。属性(全局变量)可定义可不定义,不定义默认为缺省(default)
局部变量在初始化时一定要定义初始值。属性(全局变量)不用,因为全局变量有默认值
方法的重载与重写
相同点:方法名都一样
不同点:
重写:子类继承父类重写方法,参数与返回类型必须一致,修饰符,异常返回值可以不一致
重载:同个类中同名不同功能方法,参数必须不一致,其他可以一致。
重写的修饰符不能比父类的修饰符范围更小,比如父类是public,子类是private。道理参考Object的hashCode方法,假如A重写hashcode方法,修饰符为private,则B类继承A类,B类就没有hashcode方法了
可变形参
使用方法如下
值传递
传值:把数据从一个变量赋值到另一个变量(包括String、Integer、Double等immutable类型,因为类的变量设为final属性)
// 基本数据类型
public class ParamChangeValue {
public static void main(String[] args) {
int s = 1;
System.out.println("args = [" + s + "]"); //输出1
change(s);
System.out.println("args = [" + s + "]"); //输出1,传递值不传递地址
}
private static void change(int i){
i = i* 5;
}
}
传引用:把地址从一个变量赋值到另一个变量,形参和实参指向同一个内存地址
// 引用数据类型
public class ObjectChangeValue {
public static class Score{
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
public static void main(String[] args) {
Score score = new Score();
score.setValue(1);
System.out.println(score.getValue());//输出1
change(score);
System.out.println(score.getValue());//输出2,传递的是对象的地址
}
private static void change(Score score){
score.setValue(2);
}
}
println的用法
是因为println的方法重载了,有多种
四种修饰符
如下图结构中:如果com.atguigu.java包下的Order.java类的修饰符为缺省(default)的话,com.atguigu.java1包下的OrderTest.java就无法调用Order.java而com.atguigu.java包下的类就可以调用Order.java。这就是缺省的含义,只有同一个包下的类才能调用
如下图结构中:SubOrder继承Order,Order有个属性orderProtected是protected类型的,subOrder属于不同包下的子类(可以访问protected),OrderTest属于不同包下的非子类(不可访问protected)。
构造器的概念
1.构造器可以有多个,成为构造器的重载 2.如果没有自己写空参构造方法的话,系统会默认提供一个空参构造方法,如果自己写了一个构造器(不管带不带参数),系统则不会提供默认空参构造器
继承的概念
假如父类有一个私有属性,子类不可以直接调用父类的私有属性,但是可以调用父类的get/set方法
一个子类只能有一个父类,一个父类可以有多个子类,父类也可以继承另外一个类
所有的类都直接或者间接的继承于Object类
super关键字
当一个类继承另一个类时,若他们有相同的属性,则子类调用相同名字的属性时,如果不带super则默认表示调用本类的属性而不是父类的属性
当一个类继承另一个类时,子类如果在构造器中没有写super(),则代码会默认在子类的构造器中生成一个super(),在类的构造器中,this(形参列表)和super(形参列表)只能有一个
类初始的执行顺序
class A {
private static int numA;
private int numA2;
static {
System.out.println("A的静态字段 : " + numA);
System.out.println("A的静态代码块");
}
{
System.out.println("A的成员变量 : " + numA2);
System.out.println("A的非静态代码块");
}
//如果注释了父类的无参构造器,则子类必须指定父类的构造器
// public A() {
// System.out.println("A的构造器");
// }
public A(int n) {
System.out.println("A的有参构造");
this.numA2 = n;
}
}
class B extends A {
private static int numB;
private int numB2;
static {
System.out.println("B的静态字段 : " + numB);
System.out.println("B的静态代码块");
}
{
System.out.println("B的成员变量 : " + numB2);
System.out.println("B的非静态代码块");
}
public B() {
//如果父类没有无参构造,则在子类构造器需要显示调用父类构造器
//若父类有无参构造,则可以不用显示调用super构造器
super(1);
System.out.println("B的构造器");
}
public B(int n) {
//如果父类没有无参构造,则在子类构造器需要显示调用父类构造器
//若父类有无参构造,则可以不用显示调用super构造器
super(1);
System.out.println("B的有参构造");
this.numB2 = n;
}
}
public class ClassLoad {
public static void main(String[] args) {
B anotherB = new B(1);// 思考有参构造的输出结果
}
}
多态的概念
简单理解就是:一个电器,可以多态为冰箱,电视,电灯。
所谓虚拟方法调用:多个子类都有一个共用的方法,这个方法是继承于父类的,在编译期只能使用父类来调用这个方法,但是实际执行的时候调用的是子类的方法。
equals与==的区别
自动装箱与拆箱
包装类
包装类是为了适应某些方法,某些方法的参数是Object类型,如果使用基本数据类型没法当成参数,如果把基本数据类型包装成引用数据类型后,就可以使用了
1为false是因为i和j都是new出来的,所以i和j的地址不相同,==在比较引用类型时比较的是地址,所以i不等于j。
2为true是因为n和m不是new出来的,是字面量的这种形式,在下图的cache[]中,存储了-128到127总共256个数字的数组,字面量这种行式只要是在-128到127之间的可以直接从cache中取,地址值都是一样的
3为false是因为x和y不在cache[]中,需要new出来,new出来地址就不一样了,会导致比较地址不一致出错。
static概念
Static只能修饰内部类!!!普通类不能用static修饰!!!
static可以用于描述一个类公共使用的部分,比如定义一个chinaPeople类表示中国人,有个属性叫country国家,每个对象的country都是中国,可以用static来定义
被static修饰的变量也叫类变量
单例模式
应用场景
1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件
2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.
最佳懒汉模式
public class Lazy {
private String name;
//不管初始化多少个该类的对象
//设置和获取Name属性的值都是一样的
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//懒汉模式是只有调用getInstance()方法时才会创建一个该类的实例
//原本不是线程安全的,但是加了synchronized锁和if判断后
//当需要实例化时,会先判断当前是否有该类的实例化
//如果没有则创建,如果有则返回静态的实例化,所以
//优点:线程安全,不占内存
private static Lazy lazy=null;
private Lazy(){}
public static Lazy getInstance(){
if(lazy==null){
synchronized (Lazy.class){
lazy=new Lazy();
}
}
return lazy;
}
}
普通饿汉模式
public class Hungry {
private String name;
//不管初始化多少个该类的对象
//设置和获取Name属性的值都是一样的
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//饿汉模式,一开始初始化时就已经开辟了一段内存
//因为多次初始化也都是该类的同一个对象所以
//优点:线程安全。缺点:站内存
private static Hungry hungry=new Hungry();
private Hungry(){}
public static Hungry getHungry(){
return hungry;
}
@Override
public String toString() {
return "Hungry{" +
"name='" + name + '\'' +
'}';
}
}
代码块
如果说static是用来初始化类的话,那么代码块就是来初始化对象的,static在类的加载后执行,代码块在对象的加载后执行
final关键字
final可以用在显式初始化,代码块,构造器中。
final在形参中使用时不可变,意思是调用带final常量的方法时,此方法内部不可对final常量再赋值
return ++x错误,因为final作为形参时在方法内部不可变。return x+1正确,因为x并没有发生变化
o.i++正确,因为o并没有变,只是o内部的属性变化,o内部的属性不是final。o=new Other()不对,因为o发生变化了
抽象类与抽象方法(重要)
接口与抽象类异同
相同点:都不能实例化,都需要被继承(实现)
不同点:抽象类除了不能被实例化,其他与普通类没有任何差别。接口不是一种类。抽象类可以有构造器,接口不可以有构造器。抽象类可以有protected,default,public修饰符,接口只能是public修饰符。接口定义变量时自动是static final(全局变量),抽象类定义变量时是非static final(普通变量)
抽象类的匿名子类,下面这个Person是个抽象类
接口
一个类不能实现多个父类,但可以继承多个接口
代理模式代码
/**
* 代理模式
*/
public class Proxy {
public static void main(String[] args) {
//被代理类
Server server = new Server();
//被代理类传入代理类中
ProxyServer proxyServer = new ProxyServer(server);
//代理类去做事
proxyServer.network();
}
}
//功能
interface Brower{
void network();
}
//被代理类
class Server implements Brower{
@Override
public void network() {
System.out.println("上网");
}
}
//代理类
class ProxyServer implements Brower{
private Brower brower;
public ProxyServer(Brower brower) {
this.brower = brower;
}
@Override
public void network() {
System.out.println("上网前检查");
brower.network();
}
}
简单工厂模式
汽车在CarFactory类中被生产出来,只需要调用CarFactory的getCar方法,传入要生产的车辆的名称即可生产对应的车辆。
简单工厂模式的缺点:如果引入了新的产品,则必须修改现有的代码,违背开闭原则(对于扩展开放,对于修改封闭)
public class SimpleFactory {
public static void main(String[] args) {
CarFactory.getCar("奥迪");
CarFactory.getCar("比亚迪");
}
}
class Car{
}
class Audi extends Car{
public Audi() {
System.out.println("奥迪被生产出来");
}
}
class Byd extends Car{
public Byd() {
System.out.println("比亚迪被生产出来");
}
}
//通用的工厂,一个工厂可以生产各种车
class CarFactory{
public static Car getCar(String type) {
if(type.equals("奥迪")){
return new Audi();
}else if(type.equals("比亚迪")){
return new Byd();
}else{
return null;
}
}
}
普通工厂模式
加粗样式
public class CommenFactory {
public static void main(String[] args) {
AudoProxy audoProxy = new AudoProxy();
audoProxy.productCar();
BydProxy bydProxy = new BydProxy();
bydProxy.productCar();
}
}
class Car{
}
class Audi extends Car{
public Audi() {
System.out.println("奥迪被生产出来");
}
}
class Byd extends Car{
public Byd() {
System.out.println("比亚迪被生产出来");
}
}
//工厂接口
interface InterCar{
Car productCar();
}
//奥迪工厂接口
class AudoProxy implements InterCar{
@Override
public Car productCar() {
return new Audi();
}
}
//比亚迪工厂接口
class BydProxy implements InterCar{
@Override
public Car productCar() {
return new Byd();
}
}
接口的面试题
下面的pX方法中,System.out.println(x)会报错,Java识别不出来x要调用接口A还是父类B。
可以改成System.out.println(A.x)和System.out.println(super.x)
但是接口和类中如果有同名同参数的方法时,子类在没有重写此方法的情况下,调用的是父类的方法而不是接口方法(类优先原则)
下面的代码中,play方法即重写了Playable也重写了Bounceable,Ball在接口中声明后就是public static final的,在实现类中就不可以再次new了
Java8下的接口新特性
如果子类继承了父类还实现了接口,子类想调用接口的方法就要用:接口.super.方法
内部类
在内部类中调用参数
异常
try catch基本使用
try {
String a="abc";
Integer.parseInt(a);
//1.catch (Exception e)作为父类异常不能放在最前面,除非只用Exception捕获
//如果catch (Exception e)前面还有catch语句会报错
}catch (NumberFormatException e){
//2.一般打印异常信息使用e.printStackTrace();
//System.out.println(e);
//e.printStackTrace();
}catch (Exception e){
//3.这里不会执行,因为上一个catch已经捕获异常,后面的catch就不会处理
e.printStackTrace();
}
//4.使用try catch捕获后,try catch后面的代码还会执行
System.out.println("会执行");
finally的基本使用
throws+异常类型
throw运行后,后面的代码还会继续运行吗
throw异常后只是try-catch终止,后面的代码还要继续执行,
因为你加了try-catch,所以后面的代码还要继续执行,
不加try-catch,代码就执行不了了
如何自定义异常类
finally会比throw先执行
异常总结
集合
list
list小面试题
list.remove(2)删除的是索引为2的数据,list.remove(new Integer(2))删除的是数据为2的数据
IO流
File类的基本用法
IO流
比较重要的几个流
字符流
输入流FileReader的简单使用
先在工程或者module下创建一个文本文件hello.txt
FileReader fr = null;
try {
//1.操作哪个文件
File file = new File("hello.txt");
//2.使用哪个流读取文件
fr = new FileReader(file);
//3.读取文件内容
int data;
while ((data=fr.read())!=-1){
System.out.print((char)data);
}
}catch (IOException e){
e.printStackTrace();
}finally {
//4.关闭流
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
输入流FileReader批量读取数据
File file = new File("hello.txt");
FileReader reader = new FileReader(file);
char[] data=new char[7];
int len;
while ((len=reader.read(data))!=-1){
//1.第一种写法
for (int i = 0; i < len; i++) {
System.out.print(data[i]);
}
//2.第二种写法
// String s = new String(data, 0, len);
// System.out.print(s);
}
reader.close();
输出流FileWriter的基本使用
File file = new File("hello1.txt");
//假如file文件已经存在,则不做追加操作,再创建一个新的文件
//FileWriter writer = new FileWriter(file,false);
//假如file文件已经存在,追加内容,不创建新文件
FileWriter writer = new FileWriter(file,false);
writer.write("i hava a dream \n");
writer.write("yes");
writer.close();
字节流FileInputStream与FileOutputStream的基本使用
File file=null;
File file1=null;
FileInputStream inputStream=null;
FileOutputStream outputStream=null;
try {
file = new File("logo.png");
file1 = new File("logo2.png");
inputStream = new FileInputStream(file);
outputStream = new FileOutputStream(file1);
int len;
byte[] bytes = new byte[5];
while ((len=inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len);
}
System.out.println("复制成功");
}catch (IOException e){
e.printStackTrace();
}finally {
try {
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
小总结:操作文本文件时,用字符流比较好。操作非文本文件时,用字节流比较好。如果需要复制,则使用字节流
缓冲字节流BufferStream的使用
File file=null;
File file1=null;
FileInputStream inputStream=null;
FileOutputStream outputStream=null;
BufferedInputStream bufferedInputStream=null;
BufferedOutputStream bufferedOutputStream=null;
try {
file = new File("logo.png");
file1 = new File("logo2.png");
inputStream = new FileInputStream(file);
outputStream = new FileOutputStream(file1);
bufferedInputStream = new BufferedInputStream(inputStream);
bufferedOutputStream = new BufferedOutputStream(outputStream);
int len;
byte[] bytes = new byte[5];
while ((len=bufferedInputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,len);
}
System.out.println("复制成功");
}catch (IOException e){
e.printStackTrace();
}finally {
try {
bufferedInputStream.close();
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
byte数据转成文件放到某路径下
/**
* 将byte数据存为文件
*/
public static File getFileFromBytes(byte[] b,String path) {
BufferedOutputStream stream = null;
File file = null;
try {
file = new File(path);
FileOutputStream fstream = new FileOutputStream(file);
stream = new BufferedOutputStream(fstream);
stream.write(b);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return file;
}
图片加密与解密
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = new FileInputStream("logo.png");
outputStream = new FileOutputStream("logo_secret.png");
int len;
byte[] bytes = new byte[20];
while ((len=inputStream.read(bytes))!=-1){
for (int i = 0; i < len; i++) {
//加密是字节进行^5操作
//解密是加密后的字节进行^5操作
//因为m^n^n后的结果还是m
outputStream.write(bytes[i]^5);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
记录文本中某个字符出现了多少次
FileReader fileReader =null;
try {
fileReader = new FileReader("hello.txt");
int len;
char[] chars = new char[10];
HashMap<Character, Integer> map = new HashMap<>();
while ((len=fileReader.read(chars))!=-1){
for (char c : chars) {
if(map.containsKey(c)){
map.put(c,map.get(c)+1);
}else{
map.put(c,1);
}
}
}
for (Character character : map.keySet()) {
System.out.print(character+"出现了");
System.out.println(map.get(character)+"次");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
fileReader.close();
}
转换流
使用转换流InputStreamReader读取数据到控制台
InputStreamReader reader = new InputStreamReader(new FileInputStream("hello.txt"));
int len;
char[] chars = new char[20];
while ((len=reader.read(chars))!=-1){
String s = new String(chars, 0, len);
System.out.println(s);
}
reader.close();
使用转换流复制文本文件以特定的字符集格式
InputStreamReader reader = new InputStreamReader(new FileInputStream("hello.txt"));
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("hello_gbk.txt"),"gbk");
int len;
char[] chars = new char[20];
while ((len=reader.read(chars))!=-1){
writer.write(chars,0,len);
}
reader.close();
writer.close();
对象流
使用对象流序列化数据
序列化不能处理static和transient修饰的属性
使用对象流反序列化数据
NIO
网络编程
对象中的HashCode与equals
不重写HashCode,则对象获取的hashcode值是对象内存地址的hash值(两个属性值相等的对象由于通过两次new所以地址不会一样)而不是对象属性值的hash值
不重写equals,则对象在对比过程中,对比的是内存地址而不是对象属性值
hashcode规则:
hashcode相同时,两个对象不一定相同 (equals不一定相同)
hashcode不相同时,两个对象一定不等(equals不能相同)
两个对象相同时,hashcode一定相同(对象属性值都一样了,属性值的hash码一定要一样)
两个对象不相同时,hashcode不一定不相同(不同属性值的hash码可能一致)
重写equals作用在于对比属性值而不是内存值,重写hashcode作用体现在集合中,如hashmap:
有Person类,属性值有name,此时Person person=new Person(“张三”),map.put(person,学号),然后再
Person person2=new Person(“张三”),此时若Person类不重写hashcode方法,则map.get(person2)就无法获得学号。
String三个特性
*1.String被定义为final,说明不可被继承
- 2.String内部使用char[]数组来存储数据
- 3.String实现了comparable接口表明可以比较大小,实现Serializable接口表明可以序列化。
public class MainString1 {
public static void main(String[] args) {
/**
* 1.String被定义为final,说明不可被继承
* 2.String内部使用char[]数组来存储数据
* 3.String内部存储数据的char[]被定义为finlal,数据有着不可变性
* 4.String实现了comparable接口表明可以比较大小,实现Serializable接口表明可以序列化。
*/
//1.1证明String被定义final不可变性
String s1="abc";
String s2="abc";
System.out.println(s1==s2);//true,说明s1和s2用的是同一个内存
//1.2 证明String修改字符会重新生成新的
s2+="def";
System.out.println(s1);//还是指向abc的内存
System.out.println(s2);//abcdef,开辟了新的内存
s2.replace("a","a1");
System.out.println(s1);//还是指向abc的内存
System.out.println(s2);//a1bcdef,开辟了新的内存
int i=3;
MainString1 mainString1 = new MainString1();
mainString1.change(i);
System.out.println(i);
}
public void change(int i){
i=2;
}
class Person{
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
public void setName(String name) {
this.name = name;
}
}
@Test
public void test2(){
/**
* string的实例化方式
* 1.通过字面量方式
* 2.通过new+构造器
*
* 面试题:String xxx=new String("xx");在内存中创建了几个对象
* 两个:一个是堆内存空间中的new结构,一个是char[]对应常量池的数据"xx"
*/
//通过字面量的方式,s1和s2的数据指向了方法区中字符串常量池中
String s1="abc";
String s2="abc";
//通过new+构造器,s3和s4的数据指向了堆空间中两个分开的对象,各自对应一个
String s3 = new String("def");
String s4 = new String("def");
System.out.println(s3==s4);//false,因为s3和s4指向的不是同一个对象,虽然他们两个指向的对象的数据一样
//
Person s5 = new Person("ghi");
Person s6 = new Person("ghi");
System.out.println(s5.name==s6.name);//true,虽然他们不是同一对象的name属性,但是由于是字面量赋值,所以指向的同一个数据
System.out.println(s5==s6);
System.out.println(s5.equals(s6));
}
@Test
public void test3(){
String s1="abc";
String s3="abc"+"def";
String s4=s1+"def";
String s5="abcdef";
String s6=(s1+"def").intern();
System.out.println(s3==s5);//true,没有使用到变量所以还是使用字面量
System.out.println(s4==s5);//false,使用到了变量所以要重新生成一个对象
System.out.println(s4==s6);//true,使用了intern返回的是字符串常量池的内容
}
}
String常用方法
public class StringMethod1 {
public static void main(String[] args) {
String s1=" hello world ";
System.out.println(s1.length());//int length(),返回字符串的长度
System.out.println(s1.charAt(1));//char charAt(int index),返回某索引处的字符
System.out.println(s1.isEmpty());//boolean isEmpty(),判断是不是空字符串
System.out.println(s1.toLowerCase());//将所有字符串转换为小写
System.out.println(s1.toUpperCase());//将所有字符串转换为大写
System.out.println(s1.trim());//返回去除前后空格的副本,中间空格不会去除
System.out.println(s1.equals("hello world"));//比较字符串是否相等
System.out.println(s1.equalsIgnoreCase("hello world"));//不区分大小写,比较字符串是否相等
System.out.println(s1.concat("lcy"));//将指定字符串拼接到此字符串结尾,等价"+"
System.out.println(s1.compareTo("hello word"));//比较两字符串大小,负数表明前一个小
System.out.println(s1.substring(1));//截取新字符串,从参数位置截取到最后
System.out.println(s1.substring(1, 5));//截取新字符串,从第一个参数位置到第二个参数位置,第二个参数位置不包括在内,左闭右开
System.out.println(s1.endsWith("ld "));//判断是否以参数结尾,空格也算
System.out.println(s1.startsWith(" "));//判断是否以参数开头,空格也算
System.out.println(s1.startsWith("h", 1));//判断第一个参数是否是第二个参数开始的开头,空格也算
System.out.println(s1.contains("ld"));//判断字符串是否包含参数
System.out.println(s1.indexOf("ld"));//返回指定子字符串在此字符串第一次出现的索引位置,未找到为-1
System.out.println(s1.indexOf("ld", 1));//从第二个参数开始,返回指定字符串第一次出现的位置,未找到为-1
System.out.println(s1.lastIndexOf("ld"));//返回指定字符串从右边开始数的第一次索引,未找到为-1
}
}
StringBuffer与StringBuilder
StringBuffer线程安全效率低
StringBuilder线程不安全效率高
两者初始化时默认容量为16,可以自定义大小
当要加数据时发现容量不够了会扩容,扩容机制((<<1)+1,就是两倍加一)。如果我们预知字符串经常追加数据,可以在初始化的时候,给一个较大的值。
两道简单题
public class StringQuestion {
public static void main(String[] args) {
StringQuestion question = new StringQuestion();
String str="abcdefg";
System.out.println(question.reserve(str, 1, 3));
String str1="abasdwqrcasababab";
String str2="ab";
System.out.println(question.getNum(str1, str2));
}
/**
* 给定一个字符串,将其指定位置的数据进行翻转,比如"abcdefg"改成"abedcfg";
* 思路,定义两个值,分别代表要反转数据的开头与结尾,两者循环互相换数据
*/
public String reserve(String str,int startIndex,int endIndex){
char[] chars = str.toCharArray();
for (int x=startIndex, y=endIndex;x!=y;x++,y--){
char temp=chars[x];
chars[x]=chars[y];
chars[y]=temp;
}
return Arrays.toString(chars);
}
/**
* 给定一个字符串,获取指定字符串在此字符串出现的次数。
* 比如"ab"在"abasdwqrcasab"出现的次数
* 思路:使用indexOf判断是否有,如果有从有的位置往后找,循环这样
*/
public int getNum(String str,String str2){
int count=0,index=0;
while ((index=str.indexOf(str2,index))!=-1){
count++;
index+=str2.length();
}
return count;
}
}