java面向对象(三)
继承的基本概念
继承是三大面向对象特征之一,继承无处不在,继承不仅是代码复用,精髓之处在于用抽象思维编写代码,以达到程序更强的可扩展性
继承是从已有的类创建新类的过程
- 继承是面向对象三大特征之一
- 被继承的类成为父类(超类),继承父类的类称为子类(派生类)
- 继承是指一个对象直接使用另一对象的属性和方法
- 通过继承可以实现代码重用

语法:[访问权限] class 子类名 extends 父类名{
类体定义;}
/**继承:继承是从已有的类创建新类的过程继承一个父类,只能继承非私有的数据(属性和方法)protected:访问权限修饰符,在继承关系中使用,在父类中使用protected修饰的属性或方法可以被子类继承*/
public class Test1 {public static void main(String[] args){HomeDog homeDog=new HomeDog();homeDog.print();}
}
class Dog {protected String name;private String sex;public void eat() {System.out.println("吃饭");}
}
class HomeDog extends Dog{//类的定义public void print(){System.out.println(name+"我是一只家狗,wangwang");}}
class HuskyDog extends Dog{//类的定义public void show(){System.out.println(name+"我是husky,我能跳舞");}}
protected(受保护的访问权限修饰符,用于修饰属性和方法,使用protected修饰的属性和方法可以被子类继承)
继承的限制
继承的限制约定
- Java只能实现单继承,也就是一个类只能有一个父类
- 允许多层继承,即,一个子类可以有一个父类,一个父类还可以有其他的父类
- 继承只能继承非私有的属性和方法
- 构造方法不能被继承
/**继承:继承是从已有的类创建新类的过程继承一个父类,只能继承非私有的数据(属性和方法)protected:访问权限修饰符,在继承关系中使用,在父类中使用protected修饰的属性或方法可以被子类继承创建子类对象时,对父类的构造方法也会被调用,为什么?因为子类要使用到父类的数据,那么就要通过父类的构造方法来初始化数据如果创建子类对象时使用默认的构造方法,那么父类的构造方法也会被调用如果创建子类时使用的是带参数的构造方法,那么父类的构造方法也会被调用*/
public class Test1 {public static void main(String[] args){HomeDog homeDog=new HomeDog("旺财");homeDog.print();}
}
class Dog {protected String name;private String sex;public Dog(){System.out.println("我是Dog的构造方法");}public void eat() {System.out.println("吃饭");}
}
class HomeDog extends Dog{//类的定义public HomeDog(String name){this.name=name;System.out.println("我是HomeDog的构造方法");}public void print(){System.out.println(name+"我是一只家狗,wangwang");}}
class HuskyDog extends Dog{//类的定义public HuskyDog(){System.out.println("我是HuskyDog的构造方法");}public void show(){System.out.println(name+"我是husky,我能跳舞");}}
继承小结
- 继承是发生在多个类之间
- 继承使用关键字extends
- Java只能单继承,允许多层继承
- 被继承的类叫父类(超类),继承父类的类叫子类(派生类)
- 在父类的非私有属性和方法可以被子类继承
- protected(受保护的访问权限修饰符),修饰的属性或方法可以被子类继承
- 构造方法不能被继承
- 创建对象会调用构造方法,调用构造方法不一定就是创建该类对象
- 实例化子类对象,会先调用父类的构造方法,如果父类中没有默认的构造方法,那么子类必须显示的通过super(…)来调用父类的带参构造方法,supper也只能在子类构造方法中的第一句
继承的好处: - 提高代码的复用性
- 提高代码的维护性
- 让类与类之间产生关系,是多态的前提
继承的缺点:
增强了类与类之间的耦合性
开发原则:高内聚,低耦合
子类的实例化过程
在子类进行实例化操作的时候,首先会先让其父类进行实例化操作,之后子类再自己进行实例化操作。
子类的实例化过程:
子类实例化时会先调用父类的构造方法
如果父类中没有默认的构造方法,在子类的构造方法中必须显示的调用父类的构造方法
结论:
构造方法只是用于初始化类中的字段以及执行一些初始化代码
调用构造方法并不代表会生成对象
方法的重写
方法的重写是发生在子父类继承关系中,不同于方法重载发生在同一个类中。
方法重写(overriding method)
在java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想做一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
在子类和父类中,重写方法后,在调用时,以创建的对象类型为准,会调用谁的方法。
public class Test1 {public static void main(String[] args){HomeDog homeDog=new HomeDog("旺财");homeDog.print();homeDog.eat();HuskyDog hd = new HuskyDog();hd.eat();}
}
class Dog {protected String name;private String sex;public Dog(){System.out.println("我是Dog的构造方法");}public void eat() {System.out.println("吃饭");}
}
class HomeDog extends Dog{//类的定义public HomeDog(String name){this.name=name;System.out.println("我是HomeDog的构造方法");}public void print(){System.out.println(name+"我是一只家狗,wangwang");}//重写父类的方法public void eat(){System.out.println("我是家狗,我喜欢吃骨头");}}class HuskyDog extends Dog{//类的定义public HuskyDog(){System.out.println("我是HuskyDog的构造方法");}public void show(){System.out.println(name+"我是husky,我能跳舞");}//重写父类的方法public void eat(){System.out.println("我是husky,我喜欢吃鸡肝");}}
关于方法重写的一些特性:
- 发生在子父类中,方法重写的两个方法返回值、方法名、参数列表必须完全一致(子类重写父类的方法)
- 子类抛出的异常不能超过父类相应方法抛出的异常(子类异常不能大于父类异常)
- 子类方法的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)
- 父类中的方法若使用private、static、final任意修饰符修饰,那么,不能被子类重写
为什么要重写方法?或者方法重写的目的是什么?
若子类从父类中继承过来的方法,不能满足子类特有的需求时,子类就需要重写父类中相应的方法,方法的重写也是程序扩展的体现
面试题:overloading与overriding的区别?
- overloading:方法的重载,发生在同一个类中,方法名相同,参数列表相同,与返回值无关
- overriding:方法的重写,发生在子父类中,方法名相同,参数列表相同,返回值相同,子类的访问修饰符要大于或等于父类的访问修饰符,子类的异常声明必须小于或等于父类的异常声明。如果方法被private,static,final修饰,那么不能被重写
异常处理示例:
public class Test1 {public static void main(String[] args){HomeDog homeDog=new HomeDog("旺财");homeDog.print();
// homeDog.eat();HuskyDog hd = new HuskyDog();hd.eat();}
}
class Dog {protected String name;private String sex;public Dog(){System.out.println("我是Dog的构造方法");}public void eat() throws Exception{System.out.println("吃饭");}
}
class HomeDog extends Dog{//类的定义public HomeDog(String name){this.name=name;System.out.println("我是HomeDog的构造方法");}public void print(){System.out.println(name+"我是一只家狗,wangwang");}//重写父类的方法public void eat() throws Exception{System.out.println("我是家狗,我喜欢吃骨头");}}class HuskyDog extends Dog{//类的定义public HuskyDog(){System.out.println("我是HuskyDog的构造方法");}public void show(){System.out.println(name+"我是husky,我能跳舞");}//重写父类的方法public void eat(){System.out.println("我是husky,我喜欢吃鸡肝");}}
super关键字
super可以完成以下的操作:
- 使用super调用父类中的属性,可以从父类实例处获得信息
- 使用super调用父类中的方法,可以委托父类对象帮助完成某件事情
- 使用super调用父类中的构造方法(super(实参)形式),必须在子类构造方法的第一条语句,调用父类中相应的构造方法,若不显示的写出来,默认调用父类的无参构造方法,比如:super();
super.属性 表示调用父类的属性,如果是继承过来的属性,那么super可以省略
this表示当前对象
使用super来调用父类的属性,方法和构造方法
继承的应用示例
实现一个化妆品商城中的化妆品管理
- 定义一个化妆品类(Cosmetic)
- 定义一个化妆品管理类(CosmeticManager)
(1)实现进货功能
(2)可以输出所有化妆品信息功能 - 使用继承实现一个可按单价排序输出所有化妆品的功能
- 使用继承实现一个只输出进口化妆品的功能
public class Test2 {public static void main(String[] args){ImportCosmeticManager cm=new ImportCosmeticManager();cm.add(new Cosmetic("香奈儿","进口",1000));cm.add(new Cosmetic("圣罗兰","进口",800));cm.add(new Cosmetic("大宝","国产",20));cm.add(new Cosmetic("万紫千红","国产",15));
// System.out.println(cm.cs);System.out.println("========");
// for(int i = 0; i < cm.getCs().length; i++){
// System.out.println(cm.getCs()[i]);
// }cm.printInfo();}
}
//可输出进口化妆品的管理类
class ImportCosmeticManager extends CosmeticManager{public void printInfo(){//比较两个字符串的值是否相等,不能使用==,使用equals()for (int i = 0;i<count;i++){if ("进口".equals(cs[i].getType())){System.out.println(cs[i].getInfo());}}}
}
//可按单价排序的化妆品管理类
class SortCosmeticManager extends CosmeticManager{//排序输出所有产品public void printInfo(){Cosmetic[] temp=java.util.Arrays.copyOf(cs,count);
// System.out.println(temp.length);Cosmetic c=null;for (int i=0;i<temp.length-i;i++){for (int j=0;j<temp.length-i-1;j++){if (temp[j].getPrice()>temp[j+1].getPrice()){c=temp[j];temp[j]=temp[j+1];temp[j+1]=c;}}}for (Cosmetic cosmetic:temp){System.out.println(cosmetic.getInfo());}}
}
//化妆品管理类
class CosmeticManager{/*JavaBean 或 POJO相当于是容器*/protected Cosmetic[] cs = new Cosmetic[4];protected int count = 0;//进货功能:动态扩容public void add(Cosmetic c){int size = cs.length;if (count>= size){int newLen = cs.length*2;cs=java.util.Arrays.copyOf(cs,newLen);}cs[count]=c;count++;}//输出所有产品public void printInfo(){for (int i=0;i<count;i++){System.out.println(cs[i].getInfo());}}public Cosmetic[] getCs() {return cs;}
}
//化妆品类
class Cosmetic{//实体private String name;//品牌private String type;//进口或国产private int price;//单价public Cosmetic(String name,String type,int price){this.name=name;this.type=type;this.price=price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getType() {return type;}public void setType(String type) {this.type = type;}public int getPrice() {return price;}public void setPrice(int price) {this.price = price;}// @Override
// public String toString() {
// return "Cosmetic{" +
// "name='" + name + '\'' +
// ", type='" + type + '\'' +
// ", price=" + price +
// '}';
// }public String getInfo(){return "name="+name+",type="+type+",price"+price;}}
final关键字
final关键字表示最终的,声明的属性值不能改,声明的方法不能被子类重写,声明的类无法被继承
使用final关键字完成以下的操作
- 使用final关键字声明一个常量
修饰属性或者修饰局部变量(最终变量),也称为常量 - 使用final关键字声明一个方法
该方法为最终方法,且只能被子类继承,但是不能被子类重写 - 使用final关键字声明一个类
该类就转变为最终类,没有子类的类,final修饰的类无法被继承 - 在方法参数中使用final,在该方法内部不能修改参数的值(在内部类中详解)
抽象类
抽象类可以理解为类的抽象层次,很多对象就可以抽象出一个类,很多类就可以抽象出抽象类
抽象类的基本概念
5. 很多具有相同特征和行为的对象可以抽象为一个类:很多具有相同特征和行为的类可以抽象为一个抽象类
6. 使用abstract关键字声明的类为抽象类

- 抽象类可以没有抽象方法,有抽象方法的类必须是抽象类
- 非抽象类继承抽象类必须实现所有抽象方法
- 抽象类可以继承抽象类,可以不实现父类抽象方法
- 抽象类可以有方法实现和属性
- 抽象类不能被实例化
- 抽象类不能声明为final
- 抽象类可以有构造方法
**抽象类:用abstract关键字声明的类称为抽象类很多具有相同特征和行为的对象可以抽象为一个类很多具有相同特征和行为的类可以抽象为一个抽象类*/
public class Test3 {public static void main(String[] args){Man man = new Man();man.move();Women wm=new Women();wm.move();}
}
abstract class Animal{public abstract void move();//方法的声明,抽象方法只有声明,没有实现
}
abstract class Person extends Animal{public abstract void eat();
}
//继承抽象类的具体类必须实现所有抽象方法
class Man extends Person{public void move(){System.out.println("我是男人,我爱跑步");}public void eat(){System.out.println("我是男人,我爱吃肉");}
}
class Women extends Person{public void move(){System.out.println("我是女人,我爱逛街");}public void eat(){System.out.println("我是女人,我爱吃香蕉");}
}
接口
接口是所有行为的抽象,面向接口编程,目的是让程序更加易于维护,扩展性更强,接口只定义标准,而非具体实现
接口的定义格式
interface 接口名称{
全局常量;
抽象方法;
}
接口的使用规则:
- 定义一个接口,使用interface关键字
- 在一个接口中,只能定义常量、抽象方法,JDK1.8后可以定义默认的实现方法
- 接口可以继承多个接口,extends xxx,xxx
- 一个具体类实现接口使用implements关键字
- 一个类可以实现多个接口
- 抽象类实现接口可以不实现接口的方法
- 在接口中定义的方法没有声明,访问修饰符,默认为public
- 接口不能有构造方法
- 接口不能被实例化
/**接口*/
public class Test4 {public static void main(String[] args){Girl mm=new Girl("马丽");mm.sleep();mm.eat();mm.run();}
}
interface IEat{
// public abstract void eat();//接口中只能定义抽象方法void eat();//接口中定义的方法没有声明修饰符,默认为public abstract
// public static final int NUM=10;//在接口中定义一个常量int NUM=10;}
interface IRun{void run();
}
//接口之间可以多继承(注意:类是只能单继承)
interface ISleep extends IEat,IRun{void sleep();
}
//具体类实现接口必须实现接口的所有方法
class Girl implements ISleep,IEat{private String name;public Girl(String name){this.name=name;}public Girl(){}public void sleep(){System.out.println("我爱睡觉");}public void eat(){System.out.println("我爱吃饭");}public void run(){System.out.println("我爱跑步");}
}
面向对象设计原则
10. 对修改关闭,对扩展开放
11. 面向接口编程
多态性
多态性是面向对象三大特性之一
什么是多态?
对象在运行过程中的多种形态
多态性我们大概可以分为两类:
12. 方法的重载与重写
13. 对象的多态性
例如:
//用父类的引用指向子类对象(用大的类型去接受小的类型,向上转型、自动切换)
Chicken home=new HomeChicken();
结论:
在编程时针对抽象类型的编写代码,称为面向抽象编程(或面向接口编程)
父类通常都定义为抽象类、接口
对象的多态性:
对象的多态性是从继承关系中的多个类而来,向上转型:将子类实例转为父类实例
格式:父类 父类对象=子类实例: ->自动转换
以基本数据类型操作为例:int i =‘a’;
(因为char的容量比int小,所以可以自动完成)
向下转型:将父类实例转为子类实例
格式:子类 子类对象=(子类)父类实例:强制转换
以基本数据类型操作为例:char c=(char)97;
因为整型是4个字节比char 2个字节要大,所以需要强制完成
多态性小结:
- 方法的重载与重写就是方法的多态性表现
- 多个子类就是父类中的多种形态
- 父类引用可以指向子类对象,自动转换
- 子类对象指向父类引用需要强制转换(注意:类型不对会报异常)
- 在实际开发中尽量使用父类引用(更利于扩展)
/**多态性:*/
public class Test5 {public static void main(String[] args){//用父类的引用指向子类对象(用大的类型来表示小的类型),自动转换Chicken2 hc=new HomeChicken("小鸡鸡");hc.eat();Chicken2 yc = new YeChicken("大鸡鸡");
// yc.eat();
//
// hc=yc;
// hc.eat();eat(hc);eat(yc);}//抽象(粒度) 面向抽象编程(面向接口编程)public static void eat(Chicken2 c){System.out.println("鸡吃饭");c.eat();}
}
//鸡
abstract class Chicken2{private String name;public Chicken2(){}public Chicken2(String name){this.name=name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public abstract void eat();
}
//家鸡
class HomeChicken extends Chicken2{public HomeChicken(String name){super(name);}public void eat(){System.out.println(this.getName()+",我爱吃米");}}
class YeChicken extends Chicken2{public YeChicken(String name){super(name);}public void eat(){System.out.println(this.getName()+",我爱吃虫子");}
}
instanceof关键字
instanceof关键字是验明对象正身的好利器,简单实用靠谱
instanceof是用于检查对象是否为指定的类型,通常在把父类引用强制转换为子类引用时要使用,以避免发生类型转换异常(ClassCasException)
语法格式如下:
对象 instanceof 类型 --返回boolean类型值
示例:
if (homeChicken instanceof Chicken){//...
}
该语句一般用于判断一个对象是否为某个类的实例,是返回true,否返回false
/**多态性:*/
public class Test5 {public static void main(String[] args){//用父类的引用指向子类对象(用大的类型来表示小的类型),自动转换Chicken2 hc=new HomeChicken("小鸡鸡");hc.eat();Chicken2 yc = new YeChicken("大鸡鸡");
// yc.eat();
//
// hc=yc;
// hc.eat();Chicken2 jjc=new JianJiaoChicken("尖叫鸡");eat(hc);eat(yc);eat(jjc);}//抽象(粒度) 面向抽象编程(面向接口编程)public static void eat(Chicken2 c){System.out.println("鸡吃饭");c.eat();//当我们需要把父类的实例强制转换为子类引用时,为了避免类型转换异常java.lang.ClassCastException//那么我们需要在转换之前作类型检查(判断)if (c instanceof JianJiaoChicken){//成立的条件是,对象本身及对象的父类型,都可以通过检查JianJiaoChicken jjc=(JianJiaoChicken) c;//大的类型转换为小的类型,强制转换(向下转型)jjc.eat();}}
}
//鸡
abstract class Chicken2{private String name;public Chicken2(){}public Chicken2(String name){this.name=name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public abstract void eat();
}
//家鸡
class HomeChicken extends Chicken2{public HomeChicken(String name){super(name);}public void eat(){System.out.println(this.getName()+",我爱吃米");}}
class YeChicken extends Chicken2{public YeChicken(String name){super(name);}public void eat(){System.out.println(this.getName()+",我爱吃虫子");}
}
class JianJiaoChicken extends Chicken2{public JianJiaoChicken(String name){super(name);}public void eat(){System.out.println(this.getName()+",我不吃东西");}
}
父类的设计原则
通过instanceof关键字,我们可以很方便的检查对象的类型,但如果一个父类的子类过多,这样的判断还是显得很繁琐,那么如何去设计一个父类呢?
- 父类通常情况下都设计为抽象类或接口,其中优先考虑接口,如接口不能满足才考虑抽象类
- 一个具体的类尽可能不去继承另一个具体类,这样的好处是无需检查对象是否为父类的对象
抽象类应用——模板方法模式
模板方法模式的精髓之处在于定义一个算法框架,也就是把共有部分抽象出来,这对未来底层框架设计非常有用
模板方法模式(Templete Method):定义一个操作中的算法的骨架,而将一些可变部分的实现延迟到子类中。模版方法模式使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定的步骤。
public class Test6 {public static void main(String[] args){UserManager um = new UserManager();um.action("admin","add");um.action("test","add");}
}
abstract class BaseManager{public void action(String name,String method){if ("admin".equals(name)){execute(method);}else{System.out.println("你没有操作权限,请联系管理员");}}public abstract void execute(String method);
}
class UserManager extends BaseManager{public void execute(String method){//用户是否登录的验证//验证成功后才可以执行以下操作if ("add".equals(method)){System.out.println("执行了添加操作");}else if("del".equals(method)){System.out.println("执行了删除操作");}}
}
接口应用——策略模式
策略模式是非常常用的,一个接口有多个实现,不同的实现独立封装,可以按运行时需求相互替换,可维护性就强了,新增接口实现也不会影响其它实现类
策略模式(Strategy Pattern),定义了一系列的算法,将每一种算法封装起来并可以相互替换使用,策略模式让算法独立于使用它的客户应用而独立变化
/**把可变的行为抽象出来,这样的好处是这些行为可以在真正使用时相互替换*/
public class Test10 {public static void main(String[] args){BaseService user=new UserService();user.setISave(new FileSave());user.add("user");}
}
//把可变的行为抽象出来,定义一系列算法
interface ISave{public void save(String data);
}
class FileSave implements ISave{public void save(String data){System.out.println("把数据保存到文件中。。。"+data);}
}
class NetSave implements ISave{public void save(String data){System.out.println("把数据保存到网络上..."+data);}
}
abstract class BaseService {// public abstract void save(String data);private ISave iSave;public void setISave(ISave iSave) {this.iSave = iSave;}public void add(String data) {System.out.println("检查数据合法性...");iSave.save(data);System.out.println("数据保存完毕");}
}
class UserService extends BaseService{public void save(String data){System.out.println("把数据保存到文件中。。。"+data);}
}
OO设计原则:
- 面向接口编程(面向抽象编程)
- 封装变化
- 多用组合,少用继承
Object类
所有类的祖宗Object,称为根类,所以所有类天生就有toString(),hashCode(),equals()等这些Object中继承下来的方法
object类是类层次结构的根类。
每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法
所有类都是object类的子类
- public String toString()方法
返回该对象的字符串表示
通常,toString方法会返回一个"以文本方式表示"此对象的字符串。结果应是一个简明且易于读懂的信息表达式。建议所有子类都重写此方法。 - public boolean equals(Object obj)
指示其他某个对象是否与此对象“相等”。equals方法在非空对象引用上实现相等关系:
自反性
对称性
传递性
一致性
/**Object对象*/
public class Test11 {public static void main(String[] args){Student2 s=new Student2(1,"飞飞",18);System.out.println(s);Student2 s2=new Student2(2,"备备",20);boolean b = s.equals(s2);System.out.println(b);//String str1= new String("备备");String str2= new String("备备");System.out.println(str1==str2);System.out.println(str1.equals(str2));}
}
class Student2{private String name;private int sid;private int age;public Student2(){}public Student2(int sid,String name,int age){this.sid=sid;this.name=name;this.age=age;}//重写object类中的toString方法@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", sid=" + sid +", age=" + age +'}';}//重写equals方法,来实现两个对象的比较public boolean equals(Object obj){if (this==obj){return true;}if (obj instanceof Student2){Student2 s =(Student2) obj;if (this.name.equals(s.name)){return false;}if (this.sid!=s.sid){return false;}if (this.age!=s.age){return false;}return true;}return false;}
}
- protected void finalize() throws Throwable
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写finalize 方法,以配置系统资源或执行其他清楚 - public final Class>getClass()
返回此Object的运行时类
简单工厂模式
工厂模式的应用很简单,精髓之处在于通过工厂类来获取对象,而不是直接创造对象,这样的好处在于不依赖要创建的具体对象类型,以达到解耦的目的。
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
静态代理模式
代理模式是通过代理对象来达到控制访问目标对象的目的
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。
代理模式说白了就是“真实对象”的对象,在访问对象时引入一定程度的间接性,因为这种间接性可以附加多种用途。
适配器模式
适配器模式(Adapter):将一个类的接口转化成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
OO设计原则:
- 面向接口编程(面向抽象编程)
- 封装变化
- 多用组合,少用继承
- 对修改关闭,对扩展开放
内部类
内部类的概念在一定程度上打破了类的定义,但在一些较复杂的类设计中,内部类解决了Java不能多继承的问题。
内部类就是在一个类的内部定义的类。
成员内部类格式如下:
class Outer{class Inner{}}
编译上述代码会产生两个文件:
Outer.class和Outer I n n e r . c l a s s ∗ ∗ 在外部创建内部类对象 ∗ ∗ 内部类除了可以在外部类中产生实例化对象,也可以在外部类的外部来实例化。那么,根据内部类生成的 ∗ . c l a s s 文件 : O u t e r Inner.class **在外部创建内部类对象** 内部类除了可以在外部类中产生实例化对象,也可以在外部类的外部来实例化。 那么,根据内部类生成的*.class文件:Outer Inner.class∗∗在外部创建内部类对象∗∗内部类除了可以在外部类中产生实例化对象,也可以在外部类的外部来实例化。那么,根据内部类生成的∗.class文件:OuterInner.class
“$“符号在程序运行时将替换成”.”
所以内部类的访问:通过"外部类.内部类"的形式表示
Outer out = new Outer();//产生外部类实例
Outer.Inner in = null;//声明内部类对象
in = out.new.Inner();//实例化内部类对象
1.成员内部类
/**内部类1.成员内部类:直接在类中定义的类*/
public class Test12 {public static void main(String[] args){Outer outer = new Outer();//在外部创建成员内部类的实例,因为成员内部类需要以来外部类的对象,//通常情况下,我们不建议这样来实例化内部类的对象
// Outer.Inner inner=outer.new Inner();
// inner.print();outer.innerPrint();}
}
class Outer{private String name;//建议在外部类中定义一个方法,对外提供访问内部类的接口public void innerPrint(){Inner inner = new Inner();inner.print();}//成员内部类private class Inner{public void print(){System.out.println("inner");}}
}
2.方法内部类
内部类可以作为一个类的成员外,还可以把类放在方法内定义。
注意:
1.方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化
2.方法内部类对象不能使用该内部类所在方法的非final局部变量
格式如下:
class Outer{public void doSomething(){class Inner{public void seeOuter(){}}}
}
示例:
/**内部类1.成员内部类:直接在类中定义的类2.方法内部类:在一个类中的方法内定义的类*/
public class Test12 {public static void main(String[] args){Outer outer = new Outer();//在外部创建成员内部类的实例,因为成员内部类需要以来外部类的对象,//通常情况下,我们不建议这样来实例化内部类的对象
// Outer.Inner inner=outer.new Inner();
// inner.print();outer.innerPrint();outer.show();}
}
class Outer{private String name;//-------成员内部类-----------//建议在外部类中定义一个方法,对外提供访问内部类的接口public void innerPrint(){Inner inner = new Inner();inner.print();}//成员内部类private class Inner{public void print(){System.out.println("成员内部类");}}//-------方法内部类------------//show方法的局部变量或方法的参数,实际上必须是常量 finalpublic void show(){final int x=10;class Inner2{public void print(){System.out.println("方法内部类"+x);}}Inner2 inner2 = new Inner2();inner2.print();}}
3.静态内部类
在一个类内部定义一个静态内部类:
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。静态嵌套类仅能访问外部类的静态成员和方法。
格式如下
class Outer{static class Inner{}
}
class Test{public static void main(String[] args){Outer.Inner n = new Outer.Inner();}
}
示例
/**内部类1.成员内部类:直接在类中定义的类2.方法内部类:在一个类中的方法内定义的类3.静态内部类*/
public class Test12 {public static void main(String[] args){Outer outer = new Outer();//在外部创建成员内部类的实例,因为成员内部类需要以来外部类的对象,//通常情况下,我们不建议这样来实例化内部类的对象
// Outer.Inner inner=outer.new Inner();
// inner.print();outer.innerPrint();outer.show();Outer.Inner3 inner3=new Outer.Inner3();inner3.print();}
}
class Outer{private String name;//-------成员内部类-----------//建议在外部类中定义一个方法,对外提供访问内部类的接口public void innerPrint(){Inner inner = new Inner();inner.print();}//成员内部类private class Inner{public void print(){System.out.println("成员内部类");}}//-------方法内部类------------//show方法的局部变量或方法的参数,实际上必须是常量 finalpublic void show(){final int x=10;class Inner2{public void print(){System.out.println("方法内部类"+x);}}Inner2 inner2 = new Inner2();inner2.print();}//---------静态内部类----------------static class Inner3{public void print(){System.out.println("静态内部类");}}
}
4.匿名内部类
匿名内部类就是没有名字的内部类。
匿名内部类的三种情况:
- 继承式的匿名内部类
- 接口式的匿名内部类
- 参数式的匿名内部类
在使用匿名内部类时,要记住以下原则: - 不能有构造方法,只能有一个实例
- 不能定义任何静态成员、静态方法
- 不能是public,protected,private,static
- 一定是在new的后面,用其隐含实现一个接口或继承一个类
- 匿名内部类为局部的,所以局部内部类的所有限制都对其生效
示例代码
/**内部类1.成员内部类:直接在类中定义的类2.方法内部类:在一个类中的方法内定义的类3.静态内部类*/
public class Test12 {public static void main(String[] args){Outer outer = new Outer();//在外部创建成员内部类的实例,因为成员内部类需要以来外部类的对象,//通常情况下,我们不建议这样来实例化内部类的对象
// Outer.Inner inner=outer.new Inner();
// inner.print();outer.innerPrint();outer.show();Outer.Inner3 inner3=new Outer.Inner3();inner3.print();outer.print1();outer.print2();//参数式匿名内部类outer.print3(new Eat() {@Overridepublic void eat() {System.out.println("参数式匿名内部类");}});}
}
class Outer{private String name;//-------成员内部类-----------//建议在外部类中定义一个方法,对外提供访问内部类的接口public void innerPrint(){Inner inner = new Inner();inner.print();}//成员内部类private class Inner{public void print(){System.out.println("成员内部类");}}//-------方法内部类------------//show方法的局部变量或方法的参数,实际上必须是常量 finalpublic void show(){final int x=10;class Inner2{public void print(){System.out.println("方法内部类"+x);}}Inner2 inner2 = new Inner2();inner2.print();}//---------静态内部类----------------static class Inner3{public void print(){System.out.println("静态内部类");}}//---------匿名内部类--------------------//继承式public void print1(){Cat cat=new Cat(){public void eat(){System.out.println("eat:继承式内部类");}};cat.eat();}//接口式public void print2(){Eat eat = new Eat() {@Overridepublic void eat() {System.out.println("eat:接口式匿名内部类");}};eat.eat();}//参数式public void print3(Eat eat){eat.eat();}
}
abstract class Cat{public abstract void eat();
}
interface Eat{public void eat();
}
问题:局部内部类访问局部变量必须用final修饰,为什么?
当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法被调用时会入栈,方法结束后即弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,显然已经无法使用了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了。
注意:在jdk1.8中取消了在局部内部类中使用的变量必须显示的使用final修饰,编译器默认会为这个变量加上
内部类的作用
每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度,内部类使得多继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了"多重继承"。
成员
成员内部类
静态内部类
局部
方法内部类
匿名内部类
依赖外部类对象的:成员内部类,方法内部类,匿名内部类
静态内部类不依赖外部类的对象
在项目中 优先考虑选择静态内部类(不会产生内存泄露)
递归
递归算法是一种直接或间接地调用自身算法的过程。
public class Test13 {public static void main(String[] args){int result = jiecheng2(10);System.out.println(result);}//递归算法。方法本身调用自己//1.递归必须要有出口//2.递归内存消耗大,容易发生内存溢出//3.层次调用越多,越危险public static int jiecheng2(int num){if (num == 1) return 1;return num*jiecheng2(num-1);}public static int jiecheng1(int num){int result = num;int i = num-1;do{result = result * i;i--;}while (i>1);return result;}
}
数据结构之链表
基本数据类型包装类
在 java中有一个设计的原则“一切皆对象”,Java中的基本数据类型就完全不符合这种设计思想,因为八种基本数据类型并不是引用数据类型,所以Java中为了解决这样的问题,JDK1.5以后引入了八种基本数据类型的包装类。
八种包装类分为两大类型:
-Number:integer,Short,Long,Double,Float,Byte都是Number的子类表示是一个数字。
- Object:Character,Boolean都是object的直接子类。
| 基本数据类型 | 包装类 |
|---|---|
| int | Integer |
| char | Character |
| float | Float |
| double | Double |
| boolean | Boolean |
| byte | Byte |
| short | Short |
| long | Long |
public class Test14 {public static void main(String[] args){//把基本数据类型转换为包装类,称为自动装箱Integer i1 = new Integer(10);//把包装类转换为基本数据类型,称为自动拆箱int i2 = i1.intValue();Integer i3 = 10;//建议方式Integer i4 = new Integer("123");//把数值转换成intString num1="123";int i5 = Integer.parseInt(num1);Integer i6 = Integer.valueOf(num1);}
}
转型操作
在包装类中,可以将一个字符串变为指定的基本数据类型,一般在输入数据时会是使用较多。
在Integer类中将String变为int型数据,public static int parseInt(String s)
在float类中将String变为float型数据,public static float parseFloat(String s)
享元模式
它使用共享对象,用啦尽可能减少内存使用量以及分享资讯给尽可能多的相似对象,它适合用于当大量对象只是重复因而导致无法令人接受的使用大量内存。通常对象中的部分状态是
可以共享。常见做法是将它们放在外部数据结构,当需要使用时再将它们传递给享元。
public class Test14 {public static void main(String[] args){Integer x1 = new Integer(10);Integer x2 = new Integer(10);System.out.println(x1==x2);System.out.println(x1.equals(x2));Integer x3 = new Integer(128);Integer x4 = new Integer(128);System.out.println(x3==x4);System.out.println(x3.equals(x4));Integer x5 = 10;Integer x6 = 10;System.out.println(x5==x6);System.out.println(x5.equals(x6));Integer x7 = 128;Integer x8 = 128;System.out.println(x7==x8);System.out.println(x7.equals(x8));}
}
包与访问修饰符
包用于对多个java源文件的管理,就像我们的文件目录一样。
定义一个包
package com.vince;
该语句只能出现在代码中的第一句。
package com.qfedu.sms.vo;
public class Test15 {private int id;}
OO原则:
1. 开闭原则
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭
2. 合成/聚合复用原则
新对象的某些功能在已创建好的对象里已实现,那么尽量用已有对象提供的功能,使之称为新对象的一部分
3. 依赖倒置原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该依赖细节;细节应该依赖抽象。
4. 接口隔离原则
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
5. 迪米特原则
一个对象应该对其他对象保持最少的了解
6. 里氏替换原则
所有引用基类的地方必须能透明地使用其子类的对象
7. 单一职责原则
不要存在多于一个导致类变更的原因,即一个类只负责一项职责
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
