继承是三大面向对象特征之一,继承无处不在,继承不仅是代码复用,精髓之处在于用抽象思维编写代码,以达到程序更强的可扩展性
继承是从已有的类创建新类的过程
/**
继承:继承是从已有的类创建新类的过程
继承一个父类,只能继承非私有的数据(属性和方法)
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修饰的属性和方法可以被子类继承)
继承的限制约定
/**
继承:继承是从已有的类创建新类的过程
继承一个父类,只能继承非私有的数据(属性和方法)
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,我能跳舞");
}
}
在子类进行实例化操作的时候,首先会先让其父类进行实例化操作,之后子类再自己进行实例化操作。
子类的实例化过程:
子类实例化时会先调用父类的构造方法
如果父类中没有默认的构造方法,在子类的构造方法中必须显示的调用父类的构造方法
结论:
构造方法只是用于初始化类中的字段以及执行一些初始化代码
调用构造方法并不代表会生成对象
方法的重写是发生在子父类继承关系中,不同于方法重载发生在同一个类中。
方法重写(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,我喜欢吃鸡肝");
}
}
关于方法重写的一些特性:
面试题:overloading与overriding的区别?
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可以完成以下的操作:
this表示当前对象
使用super来调用父类的属性,方法和构造方法
实现一个化妆品商城中的化妆品管理
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关键字完成以下的操作
抽象类可以理解为类的抽象层次,很多对象就可以抽象出一个类,很多类就可以抽象出抽象类
抽象类的基本概念
5. 很多具有相同特征和行为的对象可以抽象为一个类:很多具有相同特征和行为的类可以抽象为一个抽象类
6. 使用abstract关键字声明的类为抽象类
**
抽象类:用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 接口名称{
全局常量;
抽象方法;
}
接口的使用规则:
/**
接口
*/
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是用于检查对象是否为指定的类型,通常在把父类引用强制转换为子类引用时要使用,以避免发生类型转换异常(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,称为根类,所以所有类天生就有toString(),hashCode(),equals()等这些Object中继承下来的方法
object类是类层次结构的根类。
每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法
所有类都是object类的子类
/**
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方法
@Override
public 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;
}
}
工厂模式的应用很简单,精髓之处在于通过工厂类来获取对象,而不是直接创造对象,这样的好处在于不依赖要创建的具体对象类型,以达到解耦的目的。
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
代理模式是通过代理对象来达到控制访问目标对象的目的
代理模式(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.成员内部类:直接在类中定义的类
*/
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");
}
}
}
内部类可以作为一个类的成员外,还可以把类放在方法内定义。
注意:
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方法的局部变量或方法的参数,实际上必须是常量 final
public void show(){
final int x=10;
class Inner2{
public void print(){
System.out.println("方法内部类"+x);
}
}
Inner2 inner2 = new Inner2();
inner2.print();
}
}
在一个类内部定义一个静态内部类:
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。静态嵌套类仅能访问外部类的静态成员和方法。
格式如下
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方法的局部变量或方法的参数,实际上必须是常量 final
public 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("静态内部类");
}
}
}
匿名内部类就是没有名字的内部类。
匿名内部类的三种情况:
/**
内部类
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() {
@Override
public 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方法的局部变量或方法的参数,实际上必须是常量 final
public 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() {
@Override
public 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的子类表示是一个数字。
基本数据类型 | 包装类 |
---|---|
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");
//把数值转换成int
String 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. 单一职责原则
不要存在多于一个导致类变更的原因,即一个类只负责一项职责