面向对象(下)

1  处理对象

1.1  打印对象和toString()方法

toString()方法是Object类里的一个实例方法,所有的Java类都是Object类的子类,因此所有的Java对象都具有toString()方法。下面两种表达效果一样。

System.out.println(p);
System.out.println(p.toString());

toString()方法是一个非常特殊的方法,它是一个“自我描述”方法,其主要功能是:当直接打印该对象时,系统将会输出该对象的“自我描述”信息,用以告诉外界该对象具有的状态信息。
           该方法的返回值为:类名+@+hashCode,因此用户需要重写此方法来实现自我描述。

1.2  ==和equals方法

==判断两个变量是否相等时,如果两个变量是基本类型变量,且都是数值类型,则只要两个变量的值相等,就返回true。
对于两个引用类型变量,只有它们指向同一个对象时,==判断才会返回true。
==不能用于比较类型上没有父子关系的两个对象。

2  类成员

static修饰的成员就是类成员,类成员有类变量、类方法、静态初始化块三个成分,static不能修饰构造器。static修饰的成员属于整个类,不属于单个实例。

           Java类里只能包含成员变量、方法、构造器、初始化块、内部类(包括接口、枚举)5种成员。

2.1  单例类

如果一个类始终只能创建一个实例,则这个类被称为单例类。

在一些特殊场景下,要求不允许自由创建该类的对象,而只允许为该类创建一个对象。为避免其他类自由创建该类的实例,应该把该类的构造器使用private修饰,从而把该类的所有构造器隐藏起来。

根据良好封装的原则:一旦把该类的构造器隐藏起来,就需要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰。除此之外,该类还必须缓存已经创建的对象,否则该类无法知道是否曾经创建过对象,因此该成员变量需要被上面的静态方法访问,故该成员变量必须使用static修饰。

class Singleton{
	//使用一个类变量来缓存曾经创建的实例
	private static Singleton instance;
	//对构造器使用private修饰,隐藏该构造器
	private Singleton() {}
	//提供一个静态方法,用于返回Singleton实例,该方法可以加入自定义控制,保证只产生一个Singleton对象
	public static Singleton getInstance() {
		if (instance==null) {
			instance=new Singleton();
		}
		return instance;
	}
}
public class SingletonTest {

	public static void main(String[] args) {
		//创建Singleton对象不能通过构造器,只能通过getInstance方法来得到实例
		Singleton s1=Singleton.getInstance();
		Singleton s2=Singleton.getInstance();
		System.out.println(s1==s2);//将输出true
	}

}

3  final修饰符

final关键字可用于修饰类、变量、方法,表示它修饰的类、变量、方法不可改变。

3.1  final成员变量

Java语法规定:final修饰的成员变量必须由程序员显式地指定初始值。

归纳起来,final修饰的类变量、实例变量能指定初始值的地方如下:
类变量:必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定;
实例变量:必须在非静态初始化块、声明该实例变量或构造器中指定初始值,而且只能在三个地方的其中之一指定。

3.2  final局部变量

局部变量也必须显式初始化。既可以在定义时指定默认值,也可以不指定,在后面代码中赋值,但只能赋一次。

3.3  final方法和类

final修饰的方法不可被重写,如果不希望子类重写父类的某个方法,则可以使用final修饰该方法。

final修饰的类不可以有子类。

4  抽象类

4.1  抽象方法和抽象类

使用abstract来修饰;有抽象方法的类只能被定义为抽象类,抽象类可以没有抽象方法;抽象方法不能有方法体;抽象类不能被实例化;子类必须实现父类的所有抽象方法。
public abstract String test();

5  接口

5.1  接口的概念和定义

接口是从多个相似类中提取出来的规范,不提供任何实现,接口体现的是规范和实现分离的设计哲学。接口里通常是定义一组公用方法。

1.定义接口的修饰符可以是public或省略,省略的话默认采用包权限访问;
2.一个接口可以有多个直接父接口,接口只能继承接口,不能继承类;
3.接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法、默认方法或私有方法)、内部类(内部接口、枚举);这些成员都是public访问权限,可以省略;
4.接口中的静态常量默认修饰符为public static final;普通方法默认public abstract,且普通方法不能有方法体;类方法、默认方法、私有方法都必须有方法体。

public interface Demo {
	int cons=5;	//成员变量只能是常量
	void out();
	default void print() {	//默认方法需要使用default修饰
		System.out.println("默认的test()方法");
	}
	static String staticTest() {
		return "接口里的类方法";
	}
	private void foo() {
		System.out.println("foo私有方法");
	}
	private static void bar() {
		System.out.println("bar私有静态方法");
	}
}

5.2  面向接口编程

1. 简单工厂模式
有一个场景:假设程序中有个Computer类需要组合一个设备,现在有两种选择:直接让Computer类组合一个Printer,或者让Computer类组合一个Output,考虑采用哪种设计模式?如果Printer对象需要更改,则如何设计更方便?
工厂模式建议Computer类组合一个Output类型的对象,将Computer类和Printer类完全分离。通过与Output接口耦合,使用Output工厂来负责生成Output对象。

//定义一个Output接口
public interface Output {
	int MAX_CACHE_LINE=50;
	void out();
	void getData(String msg);
}

//定义Computer类
public class Computer {
	private Output out;
	public Computer(Output out) {
		this.out=out;
	}
	public void keyIn(String msg) {
		out.getData(msg);
	}
	public void print() {
		out.out();
	}
	
}

//定义Output工厂
public class OutputFactory {
    //返回Output实现类的实例,具体创建哪一个实现类的对象由该方法决定
	public Output getOutput() {
		return new Printer();
	}
	public static void main(String[] args) {
		OutputFactory of = new OutputFactory();
		Computer c=new Computer(of.getOutput());
		c.keyIn("java ee");		
		c.keyIn("java");
		c.print();
	}
}

//定义Printer类实现Output接口
public class Printer implements Output{
	private String[] printData=new String[MAX_CACHE_LINE];
	private int dataNum=0;
	public void out() {
		while(dataNum>0) {
			System.out.println("打印机打印:"+printData[0]);
			System.arraycopy(printData, 1, printData, 0, --dataNum);
		}
	}
	public void getData(String msg) {
		if(dataNum>=MAX_CACHE_LINE) {
			System.out.println("输出队列已满,添加失败");
		}else {
			printData[dataNum++]=msg;
		}
	}
}

2. 命令模式

有一个场景:某个方法需要完成一个行为,但这个行为的具体实现无法确定,必须等到执行该方法时才可以确定。

具体示例:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历数组元素时如何处理这些元素,需要在调用该方法时指定具体的处理行为。

//定义一个接口
public interface Command {
	//接口里定义的process方法用于封装“处理行为”
	void process(int[] target);
}

//数组的处理类,因无法确认处理数组的具体方法,所以利用Command参数,此参数负责处理数组的具体行为
public class ProcessArray {
	public void process(int[] target,Command cmd) {
		cmd.process(target);
	}
}

//定义数组的处理方式一
public class PrintCommand implements Command{
	public void process(int[] target) {
		for(int tmp:target) {
			System.out.println("输出目标数组的元素:"+tmp);
		}
	}
}
//定义数组的处理方式二
public class AddCommand implements Command{
	public void process(int[] target) {
		int sum=0;
		for(int tmp:target) {
			sum+=tmp;
		}
		System.out.println("数组的和为:"+sum);
	}
}

//测试
public class CommandTest {

	public static void main(String[] args) {
		ProcessArray pa=new ProcessArray();
		int[] target= {1,2,3,4};
		pa.process(target, new PrintCommand());
		System.out.println("..........");
		pa.process(target, new AddCommand());
	}

}

运行结果:

输出目标数组的元素:1
输出目标数组的元素:2
输出目标数组的元素:3
输出目标数组的元素:4
..........
数组的和为:10

 

6  内部类

7  枚举类

例如四季类,只有四个对象。这种实例有限而且固定的类称为枚举类。
用enum关键字定义枚举类;枚举类可以实现一个或多个接口,但不能显示继承父类;使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类;枚举类的构造器只能使用private修饰;枚举类的所有实例必须在枚举类的第一行显式列出,系统会自动添加public static final修饰。

7.1  枚举类入门

//定义一个Season枚举类
public enum Season {
	spring,summer,fall,winter;
}
//测试类
public class SeasonTest {
	public void judge(Season s) {
		switch(s) {//switch语句的表达式可以是枚举类
		case spring:
			System.out.println("春");
			break;
		case summer:
			System.out.println("夏");
			break;
		case fall:
			System.out.println("秋");
			break;
		case winter:
			System.out.println("冬");
			break;
		}
	}
	public static void main(String[] args) {
		for(Season s:Season.values()) {//枚举类提供的values()方法可以遍历所有的枚举值
			System.out.println(s);
		}
		new SeasonTest().judge(Season.spring);//使用EnumClass.variable调用某个实例
	}

}

运行结果:

spring
summer
fall
winter
春

7.2  枚举类的成员变量、方法和构造器

public enum Gender {
	MALE("男"),FEMALE("女");//在枚举类中列出枚举值时,就是调用构造器创建枚举类对象
	private final String name;//枚举类的成员变量尽量使用private final修饰
	//成员变量使用final修饰,必须在构造器里为其指定初始值,因此显式定义带参数的构造器
    private Gender(String name) {
		this.name=name;
	}
	public String getName() {
		return this.name;
	}
}

public class GenderTest {

	public static void main(String[] args) {
//		Gender g=Enum.valueOf(Gender.class, "MALE");//枚举类的实例只能是枚举值,不是new创建的
		Gender g=Gender.valueOf("MALE");//效果同上
		System.out.println(g+"代表:"+g.getName());
	}

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值