Java面向对象复习笔记
一、Java内存简单解析
二、属性(成员变量)VS局部变量
相同点
- 定义变量的格式相同:数据类型 变量名 = 变量值;
- 先声明,后使用。
- 变量都有其对应的作用域。
不同点
- 在类中声明的位置不同,属性是直接定义在类的一对{}内,而局部变量生命在方法内、方法形参、代码块内、构造器形参、构造器内部变量;
- 权限修饰符不同。声明属性时指明其权限,局部变量不可以使用权限修饰符。
- 属性是有默认初始化值,而局部变量没有初始化值,所以必须声明初始值。特别地,形参调用时在方法()里面直接赋值即可。
- 内存中加载的位置。属性:加载到堆空间(非static);局部变量加载到栈空间。
三、对象数组的内存解析
四、关于匿名对象的使用
注意:下面这个例子说明匿名对象是可以实现多次调用的。
public class ClassSolution {
public static void main(String[] args) {
PhoneMall p= new PhoneMall();
p.show(new Phone());
}
}
class Phone {
public void playGame () {
System.out.println("玩游戏");
}
public void chat () {
System.out.println("聊天");
}
}
class PhoneMall {
public void show (Phone phone) {
phone.playGame();
phone.chat();
}
}
这里复习一下,在Java中相同包下不同类方法调用时,如果是静态方法,直接调用即可;如果不是则应该先声明该类的对象实例再调用。
五、可变个数的形参
JavaSE5.0提供了Varargs(Variable Number of Arguments)机制,允许直接定义能和多个实参相匹配的形参。具体用法见实例。
import java.util.Arrays;
public class ClassSolution {
public static void main(String[] args) {
Phone test = new Phone();
test.show("d");
test.show("d", "da");
test.show("d", "da", "dad");
}
}
class Phone {
public void show (String ... string) {
System.out.println(Arrays.toString(string));
}
}
作为对比:
JDK5.0之前
public void show (int a, String [] string)
JDK5.0之后
public void show (int a, String ... string) // 参数可变
特别地,此种特性也可以重载,代码如下:
public void show (String s) {}
public void show (String ... s) {}
// 但是下面这种方法是和可变个数形参方法冲突的,不可以称为重载
public void show (String[] s) {}
类比可得,这里的String可变个数形参和String数组是相同的,所以想要得到每一个元素或者打印出来遍历时直接采用数组操作即可。值得注意的是:遍历计数器上限是string.length而不是string.length(),因为string被视为数组。代码如下:
import java.util.Arrays;
public class ClassSolution {
public static void main(String[] args) {
Phone test = new Phone();
test.show("d", "da", "dad");
}
}
class Phone {
public void show (String ... string) {
//System.out.println(Arrays.toString(string));
for (int i = 0; i < string.length; i++) {
System.out.print(string[i]);
}
}
}
注意:一个方法中最多含有一个可变个数形参,并且只能放在方法最后面,原因显而易见,多了或者没有放在最后都会导致编译器无法分清楚参数到底是谁的,匹配失败。
六、JavaBean拓展
JavaBean特性
- 类是公共的;
- 有一个无参的公共的构造方法(构造器);
- 有属性,且有对应的get,set方法。
这样的类称为一个JavaBean。
七、Object类
当我们输出一个对象的引用时,实际上就是调用当前对象的toString()方法。
常见的String,Date,File,包装类等都重写了toString()方法。
八、基本数据类型、包装类与String型相互转化
基本数据类型、包装类转化为String
①连接运算
int num = 10;
String str = num + "";
②调用String 重写的valueOf方法
int num = 10;
String str = String.valueOf(num);
String转化为基本数据类型、包装类
调用包装类parsexxx方法
String str = "123";
int num = Integer.parseInt(str);
九、Integer Cache
Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128到127范围内所有整数,所以如果使用自动装箱的方式,给Integer赋值的范围在这个范围之内时,可以直接使用该数组中的元素,不用再去new了,提高效率。
Integer i = 1;
Integer j = 1;
System.out.println(i == j); // true
//**************f分割线*****************
Integer m = 128;
Integer n = 128;
System.out.println(m == n); // false
十、单例模式
饿汉式
public class SingletonTest1 {
public static void main (String[] args) {
Bank bank1 = new Bank();
Bank bank2 = new Bank();
System.out.println(bank1 == bank2);// true
}
}
class Bank {
// 1.私有化类的构造器
private Bank() {
}
// 2.内部创建类的对象
// 4.所以对象也得声明为静态
private static Bank instance = new Bank();
// 3.提供公共的方法,返回类的对象(静态方法只能调用静态结构)
public static Bank getInstance () {
return instance;
}
}
懒汉式
public class SingletonTest2 {
public static void main (String[] args) {
Order order1 = new Order();
Order order2 = new Order();
System.out.println(order1 == order2);// true
}
}
class Order {
// 1.私有化类的构造器
private Order() {
}
// 2.声明当前类对象,并未初始化
// 4. 同样该对象设置成static
private static Order instance = null;
// 3.声明public,static的返回当前类对象的方法
public static Order getInstance() {
if (instance == null) {
instance = new Order();
}
return instance;
}
}
饿汉式VS懒汉式
饿汉式:
坏处:对象加载时间过长;
好处:饿汉式是线程安全的。
懒汉式:
好处:延迟对象的创建;
坏处:目前的写法线程不安全。
十一、对于属性赋值的顺序
- 默认初始化
- 显式初始化 / 代码块中赋值
- 构造器中初始化
- 有了对象以后,通过“对象.属性”或者“对象.方法”的方式进行赋值
十二、abstract注意点
- abstract不能用来修饰属性,构造器等结构;
- abstract不能用来修饰私有方法,静态方法,final方法和类。
十三、多态的应用:模板方法设计模式
解决的问题:在软件开发中实现一个算法,整体步骤固定通用,并且在父类中已经写好了;但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是模板模式。
public class ClassSolution {
public static void main(String[] args) {
Template t = new SubTemplate();
t.spendTime();
}
}
abstract class Template {
public void spendTime() {
long start = System.currentTimeMillis();
// 这里就是不确定的,易变的代码
this.code();
long end = System.currentTimeMillis();
System.out.println("运行时间为: " + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
十四、工厂模式
实现了创建者与调用者的分离。即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
核心本质
- 实例化对象,用工厂方法代替new操作;
- 将选择实现类,创建对象统一管理,从而将调用者和实现类解耦。
1.无工厂模式
public class ClassSolution {
public static void main(String[] args) {
Car a = new Audi();
Car b = new BYD();
a.run();
b.run();
}
}
interface Car {
public void run();
}
class Audi implements Car{
@Override
public void run(){
System.out.println("奥迪");
};
}
class BYD implements Car{
@Override
public void run(){
System.out.println("比亚迪");
};
}
2.简单工厂模式
public class ClassSolution {
public static void main(String[] args) {
Car a = CarFactory.getAudi();
Car b = CarFactory.getBYD();
a.run();
b.run();
}
}
interface Car {
public void run();
}
class Audi implements Car{
@Override
public void run(){
System.out.println("奥迪");
};
}
class BYD implements Car{
@Override
public void run(){
System.out.println("比亚迪");
};
}
class CarFactory {
public static Car getAudi() {
return new Audi();
}
public static Car getBYD() {
return new BYD();
}
}
这种简单的工厂模式实现了调用者和创建者隔离,但是也有缺点:对于新增产品,不修改代码的话,无法扩展,违反了开闭原则。
3.工厂方法模式
为了避免简单工厂模式的缺点,这种方法模式有一组实现了相同接口的工厂类,而简单工厂模式只有一个工厂类。
interface Car {
public void run();
}
class Audi implements Car{
@Override
public void run(){
System.out.println("奥迪");
};
}
class BYD implements Car{
@Override
public void run(){
System.out.println("比亚迪");
};
}
// 工厂接口
interface CarFactory {
Car getCar();
}
class AudiFactory implements CarFactory{
@Override
public Audi getCar() {
return new Audi();
}
}
class BYDFactory implements CarFactory{
@Override
public BYD getCar() {
return new BYD();
}
}
public class ClassSolution {
public static void main(String[] args) {
Car a = new AudiFactory().getCar();
Car b = new BYDFactory().getCar();
a.run();
b.run();
}
}
上述方法模式算是基本实现了开闭原则,当出现新的车系时只需要添加新的实现工厂类就行。
十五、Java8新特性
- 在接口中,除了抽象方法和全局静态变量之外,JDK8也允许定义静态方法和默认方法。
- 接口中的静态方法只能自己调用,不能有继承该接口的子类调用;
- 如果子类(或者实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。(类优先原则)
- 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。接口冲突。
十六、局部内部类典型应用
public Comparable getComparable() {
// 创建一个实现Comparable接口的类:局部内部类
class MyComparable implements Comparable {
@Override
public int comparaTo(Object o) {
return 0;
}
}
return new MyComparable();
}
能自己调用,不能有继承该接口的子类调用;
- 如果子类(或者实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。(类优先原则)
- 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。接口冲突。
十六、局部内部类典型应用
public Comparable getComparable() {
// 创建一个实现Comparable接口的类:局部内部类
class MyComparable implements Comparable {
@Override
public int comparaTo(Object o) {
return 0;
}
}
return new MyComparable();
}