Java学习-基础API

面向对象语法进阶

static

static基础使用

static 是什么?

  • static是静态的意思,可以修饰成员变量和成员方法。
  • static修饰成员变量表示该成员变量只在内存中只储存一份, 可以被共享访问、修改

成员变量可以分为两类

  • 静态成员变量(有static修饰,属于类、内存中加载一次): 常表示如再线人数信息、等需要被共享的信息,可以被共享访问。

在这里插入图片描述

  • 实例成员变量(无static修饰,存在于每个对象中): 常表示姓名name、年龄age、等属于每个对象的信息。
    在这里插入图片描述

static修饰对象、成员变量的内存图:
在这里插入图片描述

成员方法的分类:

  • 静态成员方法(有static修饰,归属类),建议用类名访问,也可以用对象访问
  • 实例成员方法(无static修饰,归属于对象),只能用对象触发访问
  • 使用场景:
    • 表示对象自己的行为,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
    • 如果该方法是以执行一个共用功能为目的,则可以申明成静态方法
      在这里插入图片描述

static访问注意事项:

  • 静态方法只能访问静态的成员,不可以直接访问实例成员
  • 实例方法可以访问静态的成员,也可以访问实例成员。
  • 静态方法中是不可以出现this关键字的。

static应用知识

工具类:
类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类是用来给系统开发人员共同使用的。由于工具类里面都是静态方法,知己诶使用类名访问即可,因此工具类无需创建对象,建议将工具类的构造函数进行私有。

工具类有什么好处?

  • 内部都是一些静态方法,每个方法完成一个功能
  • 一次编写,处处可用,提高代码的重用性
  • 建议工具类的构造器私有化处理

代码块:

  • 代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外
  • 在Java类下,使用{ }括起来的代码被称为代码块

代码块分为:

  • 静态代码块 :
    • 格式:static{ }
    • 特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
    • 使用场景:在类加载的时候做一些静态数据初始化操作,以便后续使用
  • 构造代码块(了解,见的少):
    • 格式:{}
    • 特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码 ,并且在构造器执行前执行
    • 使用场景:初始化实例资源

在这里插入图片描述

单例:

  • 可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。
public class SingletonDemo {
    private SingletonDemo() {
        // 把构造器私有话
    }

    /**
     * 饿汉单例模式
     */

    public static SingletonDemo instance = new SingletonDemo();
    
    /**
     * 懒汉单例模式
     */
    public static SingletonDemo instance2;
    public static SingletonDemo SingletonDemoInstance() {
        if (instance2 == null) {
            instance2 = new SingletonDemo();
        }

        return instance2;
    }
}

继承

继承的概述和特点

什么是继承?

  • java中提供一个关键字extends,用这个关键字,我们可以让一个类和另外一个类建议起父子关系。public class Student extends People {}
  • Student成为子类(派生类),People称为父类(基类或超类)
  • 作用:当子类继承父类后,就可以直接使用父类公共的属性和方法了
  • 好处:提高了代码的复用性,减少代码冗余,增强类的功能扩展性。
  • 特点:
    • 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器
    • Java是单继承模式: 一个类只能继承一个直接父类
    • Java不支持多继承,但是支持多层继承
    • Java中所有的类都是Object类的子类

继承的内存图:
在这里插入图片描述
子类是否可以继承父类的构造器?

  • 不可以的,子类有自己的构造器,父类构造器用于初始化父类对象

子类是否可以继承父类的私有成员?

  • 可以的, 只是不能直接访问

子类是否可以继承父类的静态成员?

  • 有争议的知识点
  • 子类可以直接使用父类的静态成员(共享)
  • 但是个人认为:子类不能继承父类的静态成员。(共享并非继承)

Object特点:Java中所有类,要么知己诶继承了Object,要么继承了Object,要么间接继承了Object,Object是祖宗类。

在子类方法中访问成员(成员变量、成员方法)满足:就近原则

  • 先子类局部范围找
  • 然后子类成员范围找
  • 然后父类成员范围找,如果父类还找不到则报错。

如果子父类中,出现了重名的成员,会优先使用子类的,此时如果一定要在子类中使用父类的怎么办?

  • 可以通过super关键字指定访问父类的成员

方法重写

什么是方法重写?

  • 在继承体系中,子类出现了和父类中一模一样的方法声明,我们称子类这个方法是重写的方法

@Override重写注解

  • @Override是放在重写后的方法上,作为重写是否正确的校验注解
  • 加上该注解后如果重写方法,编译阶段会出现错误提示
  • 建议重写方法都加@Override注解、代码安全、优雅

方法重写注意事项和要求

  • 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致
  • 私有方法不能被重写
  • 子类重写父类方法时,访问权限必须大于或者等于父类(暂时了解:缺省< protected<public
  • 子类不能重写父类的静态方法,如果重写会报错的
    在这里插入图片描述

子构造器的特点

  • 子类中所有的构造器默认都会先访问父类中无参的构造器,,在执行自己
  • 原因:
    • 子类在初始化的时候,有可能会使用父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
    • 子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。
  • 怎么调用父类构造器了?
    • 子类构造器第一行默认都是:super(),不写也存在

在这里插入图片描述

  • super调用父类有参数构造器的作用:初始化继承自父类的数据
  • 如果父类中没有无参构造器,只有有参构造器,会出现什么现象? 会报错。因为子类默认是调用父类的无参构造器
  • 子类构造器中可以通过书写super(…),手动调用父类的有参构造器

this和super详情: this代表类对象的引用;super:代表父类存储空间的标识
在这里插入图片描述
this(…)和super(…)使用注意点:

  • 子类通过this(…)去调用本类的其他构造器,本类其他构造器会通过super去手动调用父类的构造器,最终还是会调用父类构造器的
  • 注意:this(…)super(…)都只能放在构造器的第一行,所以二者不能共存在同一个构造器中
    在这里插入图片描述

包 、权限修饰符

什么是包?

  • 包是用来分门别类的管理各种不用类的,类似于文件夹、建包利于程序的管理和维护
  • 建包的语法格式:package公司域名倒写。包名建议全部英文小写,切具备意义
  • 建包必须在第一行,一般的IDEA工具会帮助创建
  • 导包: 想通包下的类可以直接访问,不同包下的类必须导包才能使用!导包格式:import 包名.类名;
    在这里插入图片描述

什么是权限修饰符?

  • 权限修饰符:用来控制一个成员能够被访问的范围
  • 可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。

权限修饰符的分类和具体作用范围:

  • 权限修饰符: 有四种作用范围由小到大(private -> 缺省 -> protected -> public)
    在这里插入图片描述

final、常量

final的作用

  • final关键字是最终的意思,可以修饰(类、方法、变量)
  • 修饰类: 表示该类是最终类,不能被继承
  • 修饰方法:表明该方法是最终方法,不能被重写。
  • 修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)

final修饰变量的注意

  • final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
  • final修饰的变量是引用类型:那么变量存储的地址值不能发生变化,但是地址指向的对象内容是可以发生变化的。

常量:

  • 常量是使用了public static final修饰的成员变量,必须有初始化值,而且执行过程中其值不能被改变。
  • 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。

在这里插入图片描述
常量的执行原理:

  • 在编译阶段会进行宏替换,把使用常量的地方全部替换成真实的字面量
  • 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的

枚举

  • 枚举是java中的一种特殊类型
  • 枚举的作用:是为了做信息的标志和信息的分类

定义枚举类的格式:

/**
 * 修饰符 enum 枚举名称 {
 *     第一行都是 罗列美剧类实例的名称,
 * }
 */
enum Season {
    SPRING, SUMMEP, AUTUMN, WINER,
}

反编译观察枚举的特征:
在这里插入图片描述
在这里插入图片描述
枚举的特征:

  • 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举的一个对象
  • 编译器为枚举类新增了几个方法,并且枚举类都是继承: java.lang.Enum类的,从enum类也会继承到一些方法
  • 枚举都是最终类,不可被继承
  • 枚举的构造器都是私有的(写不写都只能是私有的),因此,枚举对外不能创建对象
  • 枚举类中,从第二行开始,可以定义类的其他各种其他成员

反编译指令: javap xxx

抽象枚举:
在这里插入图片描述

抽象类

  • 在Java中abstract是抽象的意思,可以修饰类、成员方法。
  • abstract修饰类,这个类就是抽象类; 修饰方法,这个方法就是抽象方法。
    在这里插入图片描述

抽象类的注意事项、特点

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  • 类该有的成员(成员变量、方法、构造器)抽象类都可以有
  • 抽象类最主要的特点:抽象类不能创建对象,仅仅为一种特殊的父类,让子类继承并实现。
  • 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。

抽象类的场景和好处:

  • 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们设计这样的抽象类,就是为了更好的支持多态
    在这里插入图片描述

接口

  • Java提供了一个关键字interface,用这个关键字我们可以定义出一个特殊的接口:接口
    在这里插入图片描述
    在这里插入图片描述
  • 注意: 接口不能创建对象;接口用来被类实现(implements)的,实现接口的类称为实现类
    在这里插入图片描述
  • 一个类可以实现多个接口,实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。

接口的好处:

  • 弥补了类的单继承的不足,一个类同时可以实现多个接口。
  • 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。

JDK8开始,接口新增了哪些方法?

  • 默认方法: 使用default修饰,使用实现类的对象调用。
  • 静态方法: static修饰,必须用当前的接口名调用
  • 私有方法: private修饰,jdk9开始才有的,只能在接口内部被调用
  • 他们默认都会被public修饰
    在这里插入图片描述
    在这里插入图片描述

一个接口可以同时继承多个接口,便于实现类去实现:
在这里插入图片描述

接口其他注意事项(了解)

  1. 一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承
  2. 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现
  3. 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先使用父类的
  4. 一个类实现多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写方法即可。

内部类

  • 是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类
  • 场景:当一个类的内部,包含一个完整的事务,且这个事务没有必要单独设计时,就可以把这个事务设计成内部类

在这里插入图片描述

成员内部类

  • 就是类中的一个普通成员,类似前面我们学过的普通的成员变量、成员方法。注意:JDK16之前,成员内部类中不能定义静态成员,JDK16开始也可以定义静态成员了
   public class Car {
    private int num = 10;
    /**
     * 定义一个成员内部类
     */
    public class Engine {
        public void show() {
            System.out.print("内部类方位外部成员" + num  + "\n");
        }
    }

    public void method() {
        System.out.print("外部访问内部类通过对象方位" + "\n");
        new Engine().show();
    }
    public static void main(String[] args) {
        // 创建对象
        // 外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...);
        Car.Engine engine = new Car().new Engine();
        engine.show();

        // 访问内部类
        new Car().method();


    }
}

成员内部类中访问其他成员的特点:

  1. 和前面学过的实例方法一样,成员内部类的实例方法中,同样可以直接访问外部类的实例成员、静态成员。
  2. 可以在成员内部类的实例方法中,拿到当前外部类对象,格式:外部类名.this

在这里插入图片描述

静态内部类

什么是静态内部类?

  • 有static修饰的内部类,属于外部类自己持有。
  • 特点: 可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员。
  • 访问方式: 外部类名.内部类名 对象名 = new 外部类名.内部类名()
/*
	 * static 修饰的内部类
	 */
	private static int tempNum = 400;
	public static class Inner3 {
		public void show3() {
			System.out.print("静态修饰符修饰内部类访问外部类" + tempNum);
		}
		
		public static void method3() {
			System.out.print("静态修饰符修饰内部类访问外部类" + tempNum);
		}
	}
	//static 修饰内部类的访问方式
		// 外部类名.内部类名 对象名称 = new 外部类名.内部类名()
		Outer.Inner3 inner3 = new Outer.Inner3();
		inner3.show3();
		inner3.method3()

	//method3的另外一种调用模式,静态修饰,调用可以直接类名.调用
		Outer.Inner3.method3();


局部内部类

  • 局部内部类: 可以直接访问外部类的成员,在局部位置可以创建局部内部类对象,通多对象调用内部类方法,来使用局部内部类的功能
/*
	 * 局部内部类
	 */
	class Outer {
	public void method2() {
	final int cacheCount = 100;
		class TempInner {
			public void show() {

				System.out.print("局部内部类方位外部成员" + cacheCount);
			}
		}
		
		TempInner tempInner = new TempInner();
		tempInner.show();
	}
	}
	new Outer().method2();


  • 局部内部类访问局部变量:在局部内部类中访问局部对象,必须被声明为最终类型**(final修饰)**,为什么 : 因为局部变量会随着方法的调用完毕而消失,这个时候局部对象并没有立刻从堆内存中回收,还要使用这个变量,为了让数据继续使用,使用final修饰,这样在堆内存里面存的其实是一个常量值. 堆内存是在垃圾回收期空闲时期的时候回收。

匿名内部类

  • 就是一种特殊的局部内部类;所谓匿名:指的是程序员不需要为这个类声明名字。
    在这里插入图片描述
  • `特点:匿名内部类的本质就是一个子类,并会立即创建出一个子类对象。
  • 作用:用于更方便的创建一个子类对象。

interface TempClass {
	public abstract void show();
	public abstract void show2();
}

/*
	 * 匿名内部类的用法
	 */
	public void method4() {
		//第一种方法,当只有一个方法时调用,实质是创建一个类或接口的子类对象,然后调用方法
//		new TempClass() {
//			@Override
//			public void show() {
//				// TODO Auto-generated method stub
//				System.out.println("匿名内部类的第一种调用方法");
//			}
//		}.show();
		
		
		//如果有多个方法的时候我们可以使用多态来调用
		TempClass tempClass = new TempClass() {
			
			@Override
			public void show2() {
				// TODO Auto-generated method stub
				System.out.println("多个方法使用多态的方式来调用");
			}
			
			@Override
			public void show() {
				// TODO Auto-generated method stub
				System.out.println("多个方法使用多态的方式来调用");
				
			}
		};
		
		tempClass.show();
		tempClass.show2();
	}

匿名内部类用完之后,没有东西指向它,用完之后是垃圾,内存立即被回收,在android中用的比较多

匿名内部类在开发中的使用场景: 通常作为一个参数传输给方法。 需求:猫、狗参加游泳比赛
在这里插入图片描述

泛型

在这里插入图片描述

  • 定义类、接口、方法时,同时声明一个或则多个类型变量(如:<E>),称为泛型类、泛型接口、泛型方法、他们统称为泛型。

  • 作用: 泛型提供了斌阿姨阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常

  • 泛型的本质:把具体的数据类型作为参数传递给类型变量

  • 把泛型定义在类上:

//使用LinkedList实现一个栈试结构的集合
public class MyStack<E> {
	private LinkedList<E> linkedList;

	public MyStack() {
		super();
		// TODO Auto-generated constructor stub
		linkedList = new LinkedList<E>();
	}
	
	public void add(E object) {
		linkedList.addFirst(object);
	}
	
	public Object getObject() {
		return linkedList.removeFirst();
	}
	
	public boolean isEmpaty() {
		return linkedList.isEmpty();
	}

}
  • 把泛型定义在方法上
//定义一个泛型方法
	public <E> void show(E e) {
		System.out.println(e);
	}
  • 泛型定义在接口上
/*
 * 定义一个反向接口
 */
public interface Inner<E> {
	public abstract void show(E e);

}

//第一种形式,实现类的时候就知道类型了 (不常见)
class InnerDemo implements Inner<String> {
	@Override
	public void show(String e) {
		// TODO Auto-generated method stub
		
	}
}

//第二种形式: 使用的时候知道具体的类型 (常见)
class InnerDemo2<E> implements Inner<E> {
	@Override
	public void show(E e) {
		// TODO Auto-generated method stub
		
	}
}
  • 泛型的通配符 : 泛型如果明确写的时候,前后必须一致
    • ? : 表示任意类型都可以的
    • ? extend E: 向下限定,该类或该类的子类都可以 (E其子类)
    • ? super E: 向上都限定,该类或该类的父类都可以 (E其父类)
//泛型如果明确写的时候,前后必须一致
Collection<Object> collection = new ArrayList<Object>();//正确
Collection<Object> collection2 = new ArrayList<Animal>();//报错

//都不报错 ?:表示任意类型的都是可以的
Collection<?> collection3 = new ArrayList<Object>();
Collection<?> collection4 = new ArrayList<Animal>();

泛型的擦除问题和注意事项

  • 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型,这就是泛型擦除。
  • 泛型不支持基本数据类型,只能支持对象类型(引用数据类型)

常用API

Object中的几个方法

  • public native int hashCode():返回该对象的哈希码值,哈希值是根据哈希算法计算出来的一个值,这个值和地址值有关,但是不是实际地址值,根据实际地址值计算出来的一个整数),你可以理解为地址值

  • public final native Class<?> getClass(): 返回Object运行时类(字节码文件对象,同一个对象的字节码在内存中存在一次),Class类中private native String getName();返回Class对象所表示的实体对象名称

  • public String toString():返回一个以文本方式表示的字符串,建议所有子类都重写此方法

  • public boolean equals(): 指示某个对象是否和此对象相等(默认情况下比较的地址) 但是比较地址值一般来说意义不大,所以我们需要重写此方法。

    • 怎么重写?一般都是用来比较对象的成员变量值是否相等,源码如下:
    • public boolean equals(Object obj) { return (this == obj); }
  • == :基本类型: 比较的是值是否相当;引用类型: 比较的是地址值是否相同

  • equals()方法:引用类型:默认情况下比较的地址值

  • .protected void finalize() throws Throwable { }:当垃圾回收器确定不存在该对象的引用时,由对象的垃圾回收器调用此方法,用于垃圾回收,但是什么时候回收不确定

@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
			
		// 这里需要比较成员变量的值是否相等,这里的name是String类型的,引用类型的比较我们
		// 需要使用equals()比较,而不能用==比较
		
		//如果是同一个对象,我们则不需要比对其对象的属性
		if (this == obj) {
			//引用类型比对的是地址值
			return true;
		} 
		//为了提高程序的健壮性,首先判断obj是不是这个学生的一个对象
		//instanceof 判断某个对象是不是该类名的一个对象
		if (!(obj instanceof StudentTest_Object)) {
			return false;
		}
		
		
		StudentTest_Object studentTest_Object = (StudentTest_Object) obj;
		if (this.name == null) {
			if (studentTest_Object.name != null) {
				return false;
			}
			return this.age == studentTest_Object.age;
		} else {
			return this.age == studentTest_Object.age && this.name.equals(studentTest_Object.name);
		}
	}

代码参考:Java_Base_Study中的com.GY.Object包下代码

Scanner

  • JDK5后适用于获取用户的键盘输入,构造方法
/*
 * 基本格式:
 * 		public boolean hasNextXXX: 判断是否是某种类型的元素,如果不带XXX 就是判断字符串
 * 		public XXX nextXXX(): 获取该元素
 * 
 * 注意: 如果输入和获取的数据类型不一样,程序会carsh
 * 
 * 常用的两个方法:
 * 		public int nextInt():获取一个int类型的值
 * 		public String nextLine(): 获取一个string类型的值
 * 
 * 如果先获取一个数值后,在获取字符串会出现问题,主要原因: 是换行字符的问题
 * 如何解决:
 * 			把所有的数据都先按照字符串获取,然后需要什么,在转换对应的类型
 */
public class keyBroadTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner scanner = new Scanner(System.in);
		
//		int x = scanner.nextInt();
//		System.out.println(x);
		
		if (scanner.hasNextInt()) {
			System.out.println("输入的数据正确");
		} else {
			 System.out.println("输入不匹配 ");
		}
	}

}
  • 怎么样判断一个字符串是否是纯数字 的字符串:
/**
	 * 判断一个字符串中是否是纯数字
	 * @param string
	 * @return
	 */
	public static boolean isNumber(String string) {
		for (int i = 0; i < string.length(); i++) {
			//Character.isDigit(): 判断字符是否为数字
			if (!Character.isDigit(string.charAt(i))) {
				return false;
			}
		}
		
		return true;
	}

例子: 模拟用户登录,猜字小游戏功能
代码参考:Java_Base_Study中的com.GY.Scanner包下代码

String

3.1 String的构造方法介绍

String提供了11中构造方法,来创建字符串,这些方法提供不同参数:有字符串,字符数组等参数的方法
在这里插入图片描述

  • 字符串直接赋值的方式是先到字符串常量池里面去找,如果有就直接返回,没有就创建并返回。
  • 字符串一旦赋值就不能被改变(是值不变,但是引用是可以变的)

在这里插入图片描述

3.2 String的特点

String对象的特点: 以“”方式给出的字符串,只要字符串序列相同(顺序和大小写),无论在程度代码中出现几次。JVM都只会建议一个String对象,并在字符串池中维护。

String s3 = "abc";
String s4 = "abc";

在上面的代码中,针对第一行代码,JVM会建立一个String对象放在字符串池中,并给出s3参考; 第二行则让s4直接参考字符串池中的String对象,也就是说他们本质上是同一个对象

在这里插入图片描述

3.3 String的比较和常用方法

  • 使用==做比较:
    • 基本类型: 比较的是数据值是否相同
    • 引用类型:比较的是地址值是否相同
  • 字符串是对象,它比较内容是否相同,是通过一个 方法来实现的,这个方法叫equals()
    • public boolean equals(Object anObject):将此字符串与指定对象进行比较,由于我们比较的是字符串对象,所以参数直接传递一个字符串。 该方法默认也是比较地址值,但是String类重写的equals()方法,比较的是内容是否相同
String s = new String("hello") String s = "hello"
//这两种是有区别的,  前者会创建两个或者一个对象,后者会创建一个或者零个对象

在这里插入图片描述

  • String作为参数传递,效果和基本类型作为参数传递是一样的
    我们可以通过length() 方法来获取字符串的长度:
String teString = "222";
		int length = teString.length();

String类提供了连接字符串的方法,string1.concat(string2);,也可以对字符串常量使用concat()方法,但是 更常用的是使用 “+”操作符连接字符串

我们知道输出格式化数字可以使用 printf() 和 format() 方法。
String 类使用静态方法 format() 返回一个String 对象而不是 PrintStream 对象。
String 类的静态方法 format() 能用来创建可复用的格式化字符串,而不仅仅是用于一次打印输出

System.out.printf("浮点型变量的值为 " +
                  "%f, 整型变量的值为 " +
                  " %d, 字符串变量的值为 " +
                  "is %s", floatVar, intVar, stringVar);

String fs;
fs = String.format("浮点型变量的值为 " +
                   "%f, 整型变量的值为 " +
                   " %d, 字符串变量的值为 " +
                   " %s", floatVar, intVar, stringVar);

3.4 StringBuilder

如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,即耗时,又浪费空间,而这种操作还不可避免。那么有没有一种比较好的方式可以解决这个问题? 我们可以通过Java提供的StringBuilder类来解决这个问题。

StringBuilder概述: StringBuilder是一个可变的字符串类,我们可以把它看成是一个容器,这里的可变指的是StringBuilder对象中的内容是可变动 的

  • String和StringBuider的区别
    • String的内容是不可变的
    • StringBuilder: 内容是可变的
  • 当对字符串进行修改的时候,需要使用 StringBufferStringBuilder 类。和 String 类不同的是,StringBufferStringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
  • StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
    由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

数组

数组对于每一门编程语言来说都是重要的数据结构之一,Java 语言中提供的数组是用来存储固定大小的同类型元素。你可以声明一个数组变量,如 numbers[100] 来代替直接声明 100 个独立变量 number0,number1,…,number99。

4.1 数组基本操作

  • 1.声明数组变量
type[] name (type: 数组类型, name: 数名称) 首选方法
type name[] (跟上声明方法效果相同,但是不是首先方法,这个中声明方式来自CC++的声明方式)
//实例
int[] ages;
int names[];
  • 2.创建数组
//创建固定长度的数组 使用new的方式来创建 type[] name = new type[size]
	int[] arrays = new int[10]; 

上述语法做了两件事情 :使用new type[size]创建了一个数组,2.把新创建的数组的引用赋值给变量name
另外还可以使用如下方式创建,在创建的时候直接赋值:
type [] name = {value,value1,…,valueK}

  • 处理数组
    数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者 For-Each 循环(加强型循环,它能在不使用下表的情况下,遍历数组)。
double[] myList = {1.9, 2.9, 3.4, 3.5};
 
      // 打印所有数组元素
      for (double element: myList) {
         System.out.println(element);
      }
  • 多维数组
    多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组,例如:
type[][] typeName = new type[typeLength1][typeLength2];

type 可以为基本数据类型和复合数据类型,arraylength1 和 arraylength2 必须为正整数,arraylength1 为行数,arraylength2 为列数。
例如:

int a[][] = new int[2][3];

4.2 Arrays

java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。具有以下功能
* 给数组赋值:通过 fill 方法
* 对数组排序:通过 sort 方法,按升序。
* 比较数组:通过 equals 方法比较数组中元素值是否相等
* 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
* java 基本数据类型传递参数时是值传递 ;引用类型传递参数时是引用传递

public static int binarySearch(Object[] a, Object key):用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)public static boolean equals(long[] a, long[] a2):如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(ByteshortInt等)。

public static void fill(int[] a, int val):将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(ByteshortInt等)。

public static void sort(Object[] a):对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(ByteshortInt等)(源码是使用快速排序)

4.3 数组的排序

  • 冒泡排序:
/*
	 * 数组的冒泡排序代码
	 * 冒泡排序,排序array.length-1次 , 所以外层循环控制排序的次数,内存循环控制比较。每次比较从
	 * 第一个元素开始,相邻的两个元素两两比较, 看是否满足大小关系要求,如果不满足就让它俩互换。
	 * 一次冒泡会让至少一个元素移动到它应该在的位置,重复n 次
	 * 就完成了 n 个数据的排序工作。
	 */
	public static void sortArrayTest(int[] array) {
		//数组只有一个元素或则没有元素,不需要排序
		if (array.length <= 1) {
			return;
		}
		
		for (int i = 0; i < array.length -1; i++) {
			//标记数组是都还需要排序 提前退出冒泡的标志是,中间没有任何一次的,这个数组已经有序了
			boolean flag = false;
			
			for (int j = 0; j < array.length - 1 - i; j++) {
				if (array[j] > array[j+1]) {
					//交换数据
					int temp = array[j];
					array[j] = array[j+1];
					array[j+1] = temp;
					
					//如果数组中有元素更换了位置就代表数组还需要排序
					flag = true;
				}
			}
			
			if (!flag) {
				//代表没有数据交换,结束数组排序
				break;
			}
		}
	}
  • 选择排序:
/*
	 * 选择排序的基本思路:把第一个元素和后面的所有元素进行比较
	 * 从0开始,依次和后面的的元素比较,小的往前放,
	 * 第一次比较完成之后,最小出现在最小索引处
	 */
	public static void selectedSortArray(int[] array) {
		 for (int i = 0; i < array.length - 1; i++) {
			for (int j = i+1; j < array.length; j++) {
				if (array[i] > array[j]) {
					int temp = array[i];
					array[i] = array[j];
					array[j] = temp;
				}
			}
		}
	}
  • 插入排序:
/*
	 * 插入排序基本思想:将n个元素分为已有序列和无忧序列两个部分,每次处理将无有序列中的第一个元素,与
	 * 有序数列的元素从后往前逐个进行比较,找出插入位置,将该元素插入到有序数列的合适位置中
	 */
	public static void insertSortArray(int[] array) {
		for (int i = 0; i < array.length; i++) {
			//从还没有排序的部分选第一个 和已经排序好的元素进行比较
			for (int j = i; (j > 0) && (array[j] < array[j - 1]); j--) {
				int temp = array[j];
				array[j] = array[j-1];
				array[j-1] = temp;
			}
		}
	}

4.4二分查找数组中元素下标

  • 二分法查找: 要求数组是有序的,如果数组是无序的则不能使用二分查找,直接使用基本查找;如果排序,在查找是不对的,因为排序的时候已经改变了数组元素最原始的索引
/*
	 * 实现二分查找数组中的数据
	 * 查找要求: 首先要求数组是有序的,如果是无序的则采用基本查找
	 */
	public static int getIndex(int[] arr, int value) {
		int max = arr.length - 1;
		int min = 0;
		
		int mid = (max + min)/2;
		while (arr[mid] != value) {
			if (arr[mid] > value) {
				//代表往左边查找
				max = mid - 1;
			} else {
				//往右边查找
				min = mid + 1;
			}
			
			//如果查找数据,当最小索引 大于 最大索引的时候,表示查找数据不存在
			if (min > max) {
				return - 1;
			}
			
			mid = (max + min)/2;
		}
		
		return mid;
	}

Date

5.1Date的基本用法

import java.util.Date;
Date date = new Date();
System.out.println(date.toString());
  • 2.日期的比较

    • 使用 getTime() 方法获取两个日期(自1970年1月1日经历的毫秒数值),然后比较这两个值
    • 使用方法 before(),after() 和 equals()。例如,一个月的12号比18号早,则 new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回true。
    • 使用 compareTo() 方法,它是由 Comparable 接口定义的,Date 类实现了这个接口。
  • 格式化日期SimpleDateFormat
    SimpleDateFormat 是一个以语言环境敏感的方式来格式化和分析日期的类。SimpleDateFormat 允许你选择任何用户自定义日期时间格式来运行

Date date = new Date();
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println(simpleDateFormat.format(date));

这一行代码确立了转换的格式,其中 yyyy 是完整的公元年,MM 是月份,dd 是日期,HH:mm:ss 是时、分、秒。
注意:有的格式大写,有的格式小写,例如 MM 是月份,mm 是分;HH 是 24 小时制,而 hh 是 12 小时制。

5.2Calendar

我们现在已经能够格式化并创建一个日期对象了,但是我们如何才能设置和获取日期数据的特定部分呢,比如说小时,日,或者分钟? 使用Calendar,Calendar类的功能要比Date类强大很多,而且在实现方式上也比Date类要复杂一些。Calendar类是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance方法创建即可

//创建一个代表系统当前的日期Calendar对象
		Calendar calendar = Calendar.getInstance();//默认获取当前日期

Calendar类中用以下这些常量表示不同的意义,jdk内的很多类其实都是采用的这种思想
在这里插入图片描述

关于 Calender 类的一些方法用法,你可以参考标准的 Java文档

异常

异常的基本使用

异常的体系:
在这里插入图片描述

**Error:**代表的系统级别的异常(属于严重问题)也就是说系统一旦出现问题,sun公司会把这些问题封装成Error对象给出来,说白了,Error是给sun公司自己用的,不是给我们程序员用的,因此我们开发人员不用管它。
**Exprection:**叫异常,它代表的才是我们程序可能出现的问题,所以,我们程序员通常会用Exprection及其子类来封装程序出现的问题

  • 运行时异常: RuntimeException及其子类,编译阶段不会出现错误提示,运行时出现的异常(如:数组索引越界)
  • 编译时异常: 编译阶段就会出现错误提箱的。(如:日期解析异常)

抛出异常(throws)

  • 在方法上使用throws关键字,可以将方法内部出现的异常跑出去给调用者处理。
    在这里插入图片描述
/*
	 * 使用throws来抛出异常
	 */
	public static void method5() throws ParseException {
		String string = "2014-11-20";
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
		Date date = simpleDateFormat.parse(string);
	}

//调用方式 谁调用谁来处理
try {
			method5();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

编译时异常抛出,将来调用者必须处理
运行时期异常抛出,将来调用可以不用处理

throws和throw的不同:

throws: 用在方法声明后面,跟的是异常类名;可以跟多个异常类名,用逗号隔开;表示抛出异常,由该方法的调用者来处理;thows表示异常出现的一种可能性,并不一定会发生这些异常。
thorw: 只用在方法体内,跟的是异常对象名;只能抛出一个异常对象名;表示抛出异常,有方法体内的语句处理;throw则是抛出了一个异常,执行throw则一定抛出了某种异常。

捕获异常(try…catch)

  • 直接捕获程序出现的异常。
    在这里插入图片描述
/*
 * 异常: 程序出现了不正常的问题 
 * try..catch...finaly格式
 * try{
 * 		可能出现问题的代码
 * } catch(异常变量名) {
 * 		针对问题的处理;
 * }finaly {
 * 	释放资源
 * }
 * 
 * 注意:
 * 		try里面的代码越少越好。因为放在try里面的代码,java会走异常开启机制,如果代码越多。java就需要更多的资源来处理它
 * 		catch里面必须有内容,哪怕是给出一个简单的提示
 */
public class ExceptionDemo {

	public static void main(String[] args) {
		
		int a = 10;
		int b = 0;
		
		try {
			System.out.println(a / b);
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("发生异常走这里====" + e.toString());
		}
		
		System.out.println("over");
	}
}

//多个异常的处理方式
//注意: 1.能明确异常的,尽量明确异常;2.评级关系的异常谁前谁后无所谓,如果出现子父关系,父必须在后面
public static void method2() {
		int a = 10;
		int b = 0;
		
		int[] arr = {1,2,3};
		try {
			//注意:一旦try里面出现了问题,就会把问题跑出去,然后和catch里面的问题匹配,
			//一旦有匹配的,就在catch里面执行,然后结束了try...catch。继续执行后面的语句,不会再回去try里面继续执行try里面的代码了。
			System.out.println(a / b);
			System.out.println(arr[3]);
		} catch (ArithmeticException e) {
			// TODO: handle exception
			System.out.println("发生异常走这里====" + e.toString());
		} catch (ArrayIndexOutOfBoundsException e) {
			// TODO: handle exception
			System.out.println("您访问了不存在的索引===" + e.toString());
		} catchException e){
			//如果你不知道具体出现的是哪种异常,你可以使用异常的父类来接受
		}
	}

//JDK7之后新的方式
/* 
 * JDK7之后新的处理方案:
 * 			try {
 * 				
 * 			}catch(异常名 | 异常名2 | ... 变量) {
 * 				...
 * 			}
 * 
 * 这个方式虽然简洁,但是也不够好:
 * 	1:处理方式是一致的。(实际开发中,好多时候可能针对同类型的问题,给出同一个处理)
 * 	2:只能是平级关系
 */
public static void method3() {
		int a = 10;
		int b = 0;
		
		int[] arr = {1,2,3};
		try {
			System.out.println(a / b);
			System.out.println(arr[3]);
		} catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {
			// TODO: handle exception
			System.out.println("发生异常走这里====" + e.toString());
		} 
	}

编译时期异常和运行时异常的区别:
编译时期异常: Java程序必须显示处理,否则程序就会发生错误,无法通过编译
运行时异常: 无需显示处理,也可以和编译时异常一样处理

  • finally:被finally控制的语句体一定会被执行,如果执行到finally之前JVM退出了,那就不能执行了。一般用于释放资源。
/*
	 * 如果catch里面有return语句,请问finally里面的代码还会执行吗?
	 * 如果会,请问是在return前,还是return 后
	 * 
	 * 答案: 会执行, 在return前执行
	 */
	public static int getInt() {
		int a = 10;
		try {
			System.out.println(a / 0);
			a = 20;
		} catch (ArithmeticException e) {
			// TODO: handle exception
			a = 30;
			return a;
			/*
			 * return a在执行这一步的时候,这里不是返回return a而是return 30;
			 * 这个返回路径就形成了,但是他发现后面还有finally,所以继续执行finally的内容 a = 40
			 * 再次回到以前的返回路径,继续走return 30,实质上a的值确实是40
			 */
		}finally {
			a = 40;
		}
		
		return a;
	}
	//所以可见 finally的代码 准确的说是在return的中间执行的 

自定义异常

  • Java无法为这个世界上全部的问题都提供异常类来代表,如果企业自己的某种问题,想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类了

自定义异常的种类:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

异常有什么作用?

  1. 异常是用来查询系统Bug的关键参考信息!
  2. 异常可以作为方法内部的一种特殊返回值,以便通知上层调用者底层的执行情况!

异常的注意事项:

1.子类重写父类方法时,子类的方法必须抛出相同的异常或者父类异常的子类。
2.如果父亲抛出多个异常,子类重写父类时,只能派出相同或者它的子集,子类不能抛出父类没有的异常
3.如果被重写的方法没有异常抛出,那么子类方法绝对不能抛出异常,如果子类方法内有异常发生,那么子类中只能try,不能throws

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值