三、 java面向对象高级语法
1、封装类(基本数据类型的包装类)(非重点,掌握运用即可)
理解:java中万物皆对象,但是数据分为引用类型数据和基本数据类型,为了实现将基本数据类型抽象成类和对象,产生了基本数据类型的封装类(包装类)。
内容:基本数据类型—对应的封装类;byte—Byte;short—Short;int—Integer;long—Long;char—Character;float—Float;double—Double;boolean—Boolean;其中只有Integer和Character命名和相应的基本数据类型有所变化,其他的都是首字母变成大写,而Integer、Byte、Short、Long、Float、Double都是Number类的子类,Character和Boolean是Object类的直接子类。
特点:以上的所有封装类都是final修饰的,不能被继承。
基本数据类型和包装类的转换:A)基本数据转成包装类,通过包装类的构造方法实现,除了Character,其他的包装类都有一个类似的构造器,参数为字符串,字符串内容为相应的基本数据类型的字符串形式;B)包装类转基本数据类型:包装类的方法typeValue()//type为相应的基本数据类型;C)java1.5之后允许封装类和基本数据类型自动装箱和自动拆箱,即允许把基本数据类型直接赋值给对应的包装类或者Object类对象(自动装箱),也可以把包装类对象直接赋值给基本数据类型变量(自动拆箱)。D)基本数据类型和String之间的转换,除了Character之外其他包装类都提供了parseXxx(String x)的方法将字符串转成基本数据类型,String类的静态方法valueOf(基本数据类型类型)返回基本数据对应的String。
Wrapper .java
public class Wrapper {
public static void main(String[] args) {
//两种常用的构造封装类的方法
int i = 12;
Integer j = new Integer(i);
System.out.println(j);//可以直接输出对象j,因为封装类都重写了toString方法
//Character只能用上述方法构造
Integer k = new Integer("123");
System.out.println(k.intValue()); //也可以用xxxValue的方法
System.out.println("*****************************");
//自动拆箱、自动装箱
Integer l = 10; //自动装箱,引用类型用基本数据类型赋值
int m = l; //自动拆箱,基本数据类型直接用引用类型赋值
System.out.println(l + "\t" + m);
System.out.println("*****************************");
//string 与包装类的转换
String str = "3.1415926";
double d = Double.parseDouble(str); //string转基本数据类型
System.out.println(d);
String s = String.valueOf(d); //基本数据类型转String,若是封装类直接用toString
System.out.println(s);
}
}
2、Object类
概述:Object类是所有类的根父类,若一个类定义时未显示的用extends声明父类,则其默认继承Object为其直接父类。一切数据类型都可以用Object接收。如:public class Person{}等价于public class Person extends Object {}。
常用方法:boolean equals(Object obj)、 int hashCode()、 String toString()。
equals方法:equals()方法是Object类的方法,由于所有类都继承Object类,也就继承了equals()方法。只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。格式:obj1.equals(obj2)。特例:当用equals()方法进行比较时,对类File、String、Date及封装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象;原因:在这些类中覆盖了equals()方法。若类中重写了equals方法进行比较时,建议重写hashCode方法,equals比较相同,则hashcode肯定相同,hashCode相同,equals不一定相同。 在java的集合中,判断两个对象是否相等的规则是:首先,判断两个对象的hashCode是否相等,如果不相等,认为两个对象也不相等,如果相等,则判断两个对象用equals运算是否相等,如果不相等,认为两个对象也不相等,如果相等,认为两个对象相等。
toString:返回对象的字符串表示。直接打印时,参数为对象时,调用对象的toString方法,建议重现toString方法,自定义输出格式。否则输出:权限定类名+@+十六进制hashCode。
equals和“==”区别:通常,equals比较的是两个对象是否是同一个对象,也就是说物理地址相同否,是类中的方法,比较的是引用类型数据,而“==”比较的是基本数据类型的值是否相等,比价内存中放入的具体值,而不是内存的地址。也就是说,比较引用型数据,两者相同(不重写的话)
TestObjectMethod.java (equals和==区别,String缓冲池问题)
public class TestObjectMethod {
public static void main(String[] args) {
//object中,equals方法不是空的,已经写了,但是常常需要根据需求重写
Object obj1 = new String("AA");
Object obj2 = new String("AA");
System.out.println(obj1.equals(obj2)); //true,重写后比较的是内容
System.out.println(obj1 == obj2); //false
//==比较引用型数据时比较对象是否相同,比较基本数据类型,比较变量值是否相等
Person p1 = new Person(12,"xiaoming");
Person p2 = new Person(12,"xiaoming");
System.out.println(p1.equals(p2)); //false
//未重写equals方法,默认继承Object的,是比较对象是否相同,通常重写,该成比较对象的某些属性是否相等
System.out.println(p1.toString());
//重写toString方法,输出:[年龄:12 姓名:xiaoming]
//String缓冲池问题
String s1 = new String("AAAA");
String s2 = new String("AAAA");
String s3 = "AAAA";
String s4 = "AAAA";
System.out.println(s1.equals(s2));//true,1、2内容相同
System.out.println(s1.equals(s3));//true,1、3内容相同
System.out.println(s1==s2);//false,1、2对象不同
System.out.println(s3==s1);//false,1、3对象不同
System.out.println(s3==s4);//true,3、4对象相同,直接=出来的字符串(不是new)会放到缓冲池中
System.out.println(s3.equals(s4));//true,3、4内容肯定相同,对象都是一个
}
}
class Person {
private int age;
private String name;
public Person(int age,String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "[年龄:"+ age + " 姓名:" + name + "]";
}
}
3、代码块
定义:代码块指{}括起来的一段代码,代码块中的变量只在本体中有效。
分类:A)普通代码块:方法或语句中的代码块。B)构造代码块:直接定义在类中的代码块,优先于构造方法执行,每次实例对象都会执行。C)静态代码块:Static修饰的构造代码块:优先于主方法main执行,不管创建多少对象,只执行一次。D)同步代码块,后续学习。
BlockRunOrder.java
public class BlockRunOrder {
{
System.out.println("构造代码块..."); //2、优先于构造器,每次创建对象都运行
}
public BlockRunOrder() {
System.out.println("构造器...."); //3
}
static{
System.out.println("静态代码块..."); //1、且只运行一次,不论new多少个对象
}
public static void main(String[] args) {
new BlockRunOrder();
new BlockRunOrder();
}
}
4、单例模式 Singleton
设计模式:设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
单例模式(单子模式):所谓类的单态设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
实现原理:1)如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new 操作符在类的外部产生类的对象了;2)但在类内部仍可以产生该类的对象。3)因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象;4)静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
懒汉式:只给出对象变量,不创建对象,使用的时候再创建。线程不安全。
饿汉式:随着类的加载创建对象。
补充:实际上通过private构造器实现单例,不安全,因为可以通过反射可以调用到私有化的构造器,此时有几种改善方法:1)土方法,通过抛出异常来抵抗反射第二次运行构造器。(具体:单子类中添加属性,private static int count = 0; 可见count是共享的数据,然后在构造器中添加这么一句:if(count == 1) {throw new RuntimeException("只能初始化一次!"); } count++;) 2)使用枚举类型强化单例
Singleton.java
public class Singleton{
public static void main(String[] args) {
Singleton1 s1 = Singleton1.getInstance();
Singleton1 s2 = Singleton1.getInstance();
Singleton2 s3 = Singleton2.getInstance();
Singleton2 s4 = Singleton2.getInstance();
System.out.println(s1==s2); //true
System.out.println(s3==s4); //true
}
}
//懒汉式
class Singleton1 {
private static Singleton1 instance = null;
private Singleton1() {
// 构造器
}
public static Singleton1 getInstance() {
if (instance == null) {
instance = new Singleton1();
}
return instance;
}
}
//饿汉式
class Singleton2{
private static final Singleton2 instance = new Singleton2();
private Singleton2(){
}
public static Singleton2 getInstance(){
return instance;
}
}
//这种单例能防止反射暴力运行私有化的构造器
class SingletonSafe {
private static int count = 0;
private static final Singleton INSTANCE = new Singleton();
private SingletonSafe() {
if(count == 1)
throw new RuntimeException("只能初始化一次!");
count++;
// 构造器二次运行时,抛出异常
}
public static Singleton getInstance() {
return INSTANCE;
}
}
//另一种安全的单例模式,枚举类实现
//除了这种单例模式,以上所有的单例模式为了使Singleton能够序列化,除了实现标记接口Serializable外,
//还需增加类似private Object readResolve(){return INSTANCE;}的方法,防止反序列化时生成“假冒”的单例类:
enum stu {
xiaomi;
private stu(){}
}
5、final关键字
使用:A)final可修饰类、方法、变量。B)final修饰的类不能被继承,但是可以继承其他类。C)final修饰的方法不能被覆写,但是可以覆写父类的方法。D)final修饰的变量成为常量,只能被赋值一次。E)内部类在局部时,只可以访问被final修饰的局部变量。F)final修饰的引用数据类型,表示该变量的引用不能变,而不是该变量的值不能变。
G)final标记的成员变量必须在声明的同时或在每个构造方法中显式赋值,然后才能使用。
6、抽象类
产生:有时,父类只能知道子类应该具有某种方法,但是不知道子类会具体如何实现这中方法,此时,在父类中定义该方法为抽象方法,abstract修饰。而有抽象方法的类就是抽象类,含有抽象方法的类必须被声明为抽象类,但是抽象类可以含有普通方法,甚至abstract修饰的抽象类,可以没有抽象方法。
特征:抽象方法只有返回值类型,和方法签名,而没有方法体;通过abstract修饰的类必须子类强制覆写其方法。抽象类不能创建实例(new)。需要子类覆写所有父类的抽象方法之后,才能创建子类对象,否则子类也视为抽象类,常见的抽象类InputStream,Writer。
不能用abstract修饰私有方法,构造方法,静态方法,它需要子类覆写。
补充:模板设计模式(Template):又叫模板方法模式,在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情冴下,重新定义算法中的某些步骤。也就是说,设计一个抽象的父类,提供多个子类的通用方法,并把一个或是多个抽象方法留给子类去实现,这就是模板设计模式。
AbstractClass.java
@SuppressWarnings("unused")
public abstract class AbstractClass {
private int age;
private String name;
public static void main(String[] args) {
AbstractClass a = new AbstractClass(){
//不能创建实例,除非重写所有抽象方法,此处类似匿名内部类实现接口的方法
};
AbstractClass b = new B(); //多态创建子类对象
}
}
class B extends AbstractClass {
}
Template.java
public class Template {
public static void main(String[] args) {
Rengle s = new Rengle(12);
s.findArea();
s.print();
Circle c = new Circle(4);
c.findArea();
c.print();
//area is : 144.0
//area is : 50.24
}
}
//计算的模板,提供共同解决步骤,具体实现交给具体类
abstract class FindArea{
abstract double findArea();
public void print(){
System.out.println("area is : " + findArea());
}
}
//具体的类实现其具体的方法
class Rengle extends FindArea{
private double i;
public Rengle(double i) {
this.i = i;
}
@Override
double findArea() {
return i*i;
}
}
class Circle extends FindArea{
private double i;
public Circle(double i) {
this.i = i;
}
@Override
double findArea() {
return i*i*3.14;
}
}
7、接口(Interface)
定义:接口是一种特殊的更彻底的抽象类,只提供需要实现的方法, 而不关心方法的具体实现过程。分离了规范和实现,增强系统的可扩展性和维护性。
格式:修饰符 interface 接口名 (extends 父接口1,父接口2……)。接口中没有构造器,不能实例化,接口只能继承接口,不能继承类。接口中的方法全是抽象方法,默认修饰为public abstract,接口中的字段全是全局常量,被默认修饰为public static final。接口的主要内容为全局常量、公共的抽象方法、内部类(内部类,内部接口,内部枚举类)。
使用:接口可以多继承其他接口,但不能继承类,类可以实现多个无关接口,且类实现接口的implements语句必须在extends语句后面,实现接口的方法必须是public的。接口和抽象类一样不能实例化,但是可以利用多态,声明引用类型的变量,但实际指向(建立)实现类对象(子类对象)。
工厂模式:构建一个工厂,在里面进行生产,用的时候直接拿,屏蔽不同子类的实现差异。简单工厂模式就是由一个工厂类根据传入的参数决定创建哪一种的产品类。工厂方法模式(Factory Method pattern)是最典型的模板方法模式(Templete Method pattern)应用。
适配器模式:使用一个类,但是它的接口不完全符合我的使用要求,我只想用接口中部分方法,不想覆写其他方法,使用适配器作为中间过渡(如Swing的事件监听中的应用)。
接口与抽象类的对比:
SimpleFactory.java
//工厂模式
public class SimpleFactory {
public static void main(String []args){
Icarfactory factory=new bmwFactory(); //创造特有的工厂
Icar bwm= factory.createCar(); //从工厂中创造产品
bwm.docar();
factory=new buickFactory();
Icar buick= factory.createCar();
buick.docar();
}
}
interface Icar{
public void docar();
}
class bwm implements Icar{
public void docar(){
System.out.println("我是宝马,别摸我");
}
}
class buick implements Icar{
public void docar(){
System.out.println("我是别克,很酷");
}
}
interface Icarfactory{
public Icar createCar();
}
class bmwFactory implements Icarfactory{
public Icar createCar(){
return new bwm();
}
}
class buickFactory implements Icarfactory{
public Icar createCar(){
return new buick();
}
}
Adapter.java
//适配器模式:只想使用接口中的部分方法,使用适配器中间过渡
public class Adapter {
public static void main(String[] args) {
Animal pig = new Pig();
pig.basketball(); // 没有作用,因为没有具体的方法体
pig.eat(); // a pig is eating...
}
}
//这是一个接口,但是不是所有动物都能完成这些活动
interface Animal{
void basketball();
void eat();
void chat();
void beEat();
}
//创造一个适配器,不实现接口中的方法,只为子类提供覆写
class Adapt implements Animal{
public void basketball() {}
public void eat() {}
public void chat() {}
public void beEat() {}
}
class Pig extends Adapt{
//pig不能chat,不能basketball,所以只需要覆写eat和beEat就可以了
@Override
public void eat() {
System.out.println("a pig is eating...");
}
@Override
public void beEat() {
System.out.println("oh myGod,i'm die...");
}
}
//其他的动物类 类似,覆写其能实现的活动即可
8、匿名内部类
使用:只适合一次使用的类,不能是抽象类,因为在创建匿名内部类的时候,会立即创建匿名内部类的对象。匿名内部类不能定义构造器,因为他没有类名。格式:
new 父类构造器(实参列表)或接口()
{
//匿名内部类的类体部分
}
内部类:在类的内部声明的一个类。注意内部类和外部类(相对)的互相访问。
InnerClassAccess.java
public class InnerClassAccess {
public static void main(String[] args) {
new ClassRoom();//此处会创建一个classRoom和一个SleepStu
System.out.println("**************");
new ClassRoom().new SleepStu(); //从外部创建内部类对象,
//上句会创建一个classRoom和两个SleepStu
}
}
@SuppressWarnings("unused")
class ClassRoom {
private int numberStu = 40;
private Object[] Student;
private Object teacher;
public ClassRoom() {
System.out.println("教室里的人数为:" + this.numberStu);
System.out.println("睡觉人数为:"+ new SleepStu().numberStu);
}
class SleepStu{
private int numberStu = 12;
private String name = "xiaoming";
SleepStu(){
System.out.println("***教室里的人数为:" + ClassRoom.this.numberStu);
System.out.println("***睡觉人数为:"+ this.numberStu);
}
}
}
9、枚举类
使用enum声明,默认直接继承了java.lang.Enum类,而不是Object类。枚举类的对象时固定的,实例个数有限,不可以在new,枚举元素必须位于枚举类体中的最开始部分,枚举元素后用分号‘;’与其他成员分开。枚举类的构造方法默认为private的。一但枚举类的对象后面有花括号{},那么该对象实际就是枚举匿名内部类对象。
方法:A)所有枚举类都有一个静态的value方法,返回该枚举类的所有对象组成的数组,便于遍历所有枚举对象。B)所有枚举类都有一个静态的valueOf(String name)方法,返回枚举类中对象名等于name的那个对象。C)枚举类不能new,即使反射也不行。
实现接口:枚举类实现接口的有两种方法,如下例。
TestEnum.java
public class TestEnum {
public static void main(String[] args) {
color2 c2 = color2.blue;
c2.paint();
color c1 = color.red;
c1.paint();
}
}
interface a {
void paint();
}
// 枚举两种实现接口的方法
enum color implements a {
yellow, red, blue;
public void paint() {
System.out.println("paint!...");
}
}
enum color2 implements a {
yellow {
public void paint() {
System.out.println("yellow is paint...");
}
},
blue {
public void paint() {
System.out.println("blue is paint...");
}
},
red {
public void paint() {
System.out.println("red is paint...");
}
};
}