浅显回顾 Java 面向对象的最后方面的知识

今天主要回顾一下 Java 面向对象的最后一部分的知识,算是对面向对象的一个总结了吧!

先来讲两个关键字吧!

 
1. abstract
如果一个类的所有子类都对这个类中的某个方法做了重写,那么这个时候这个类中的对应方法可以不定义方法体,需要用abstract修饰方法,从而成为一个抽象方法。抽象方法所在的类必须是抽象类
抽象类不能创建对象。 --- 抽象方法一定是抽象类,但抽象类中不一定有抽象方法。
抽象类一定不是最终类,因为最终类不能被继承。
注意:任何一个类都有构造方法
抽象方法没有方法体,一定要被重写。
抽象方法可以定义被 static/final/private 修饰吗? --- 不行
抽象方法中一定不能定义在最终类中。
如果一个类中的抽象方法用的是默认权限,对子类有什么要求? --- 要求父子类要同包
package abstractx;

public class AbstractDemo {
    
    //不允许被实例化
    //Pet p = new Pet();
    //p.eat();
    
    //创建的是匿名内部类
    Pet p = new Pet(){

        @Override
        public void eat() {
            // TODO Auto-generated method stub
            
        }
        
    };
    
}

//不想让这个类实例化,就定义一个抽象类
abstract class Animal{}

//抽象类
abstract class Pet{
    
    //
    public Pet(){}
    
    //抽象方法,因为长得像方法,但是没有方法体
    public abstract void eat();/*{
        //System.out.println("在吃东西~~~");//有没有方法体都不重要了
    }*/
    
    public void drink(){
        System.out.println("在喝水中~~~");
    }
}

//子类继承抽象类之后必须重写其中的抽象方法,除非子类也是抽象类
class Cat extends Pet{
    
    @Override
    public void eat(){
        System.out.println("这只猫在吃土~~~");
    }
    
}

class Dog extends Pet{
    
    @Override
    public void eat(){
        System.out.println("这只狗在吃猫~~~");
    }
    
}
 
练习:定义一个类表示形状,提供获取周长和面积的方法,然后给这个类提供子类:矩形 - 正方形,椭圆 - 圆形
package abstractx;

public class AbstractExer {
    public static void main(String[] args) {
        Shape f;
        f = new Rectangle(3,4);
        System.out.println(f.getGirth() + "," + f.getArea());
        f = new Oval(4,5); 
        System.out.println(f.getGirth() + "," + f.getArea());
    }
}

abstract class Shape{
    
    public abstract double getGirth();
    public abstract double getArea();
}

//代表矩形
class Rectangle extends Shape{

    double width;
    double height;
    
    public Rectangle(double width, double height){
        this.width = width;
        this.height = height;
    }
    @Override
    public double getGirth() {
        return 2 * (width + height);
    }

    @Override
    public double getArea() {
        // TODO Auto-generated method stub
        return width * height;
    }
    
}

//正方形
class Square extends Rectangle{

    private double width;
    public Square(double width) {
        super(width, width);
    }
    
}

//椭圆形
class Oval extends Shape{
    
    private double a;
    private double b;
    public static final double PI = 3.14;
    
    public Oval(double a, double b){
        this.a = a;
        this.b = b;
    }
    
    @Override
    public double getGirth() {
        // TODO Auto-generated method stub
        return PI * a * b;
    }

    @Override
    public double getArea() {
        // TODO Auto-generated method stub
        return PI * (a + b);
    }
    
}

//圆形
class Circle extends Oval{

    private double r;
    
    public Circle(double r){
        super(r, r);
    }

}
 
2. interface
(作用:作为模板或者是协议、约束等来使用)
接口中定义的都是抽象方法。类和接口之间用的是 implements 关键字来产生关联 --- 实现。类和实现接口之后需要重写接口中所有的抽象方法
接口不允许被实例化,也没有构造方法。
在 Java 中,支持的是类和接口之间的多实现 --- 一个类可以实现多个接口
在 Java 中,支持接口之间的多继承(接口可以继承接口),
在 JDK1.8 中,接口中允许定义实体方法 -- 这个是实体方法必须用 default 修饰
package cn.tedu.interfacex;

import java.io.Serializable;

public class InterfaceDemo {
    
    public static void main(String[] args) {
        
        System.out.println(Shape.girth);
        //Shape.girth = 1;//说明是使用final修饰的
        
        //接口不允许实例化
        //Shape s = new Shape();
    }
}

interface Shape extends Cloneable,Serializable{
    
    double girth = 0;//默认是用的 static,final
    
    //public Shape(){}不允许使用
    
    //接口中的方法默认public abstract修饰
    /*public abstract */double getGirth();
    public abstract double getArea();
    
}

//利用implements关键字让类和接口产生了联系 --- 实现
//Rectangle实现了 Shape接口
/*class Rectangle implements Shape, Cloneable{
    
}
*/

interface E{
    void main();
}

/*class B implements E{

    void main() {//报错了,范围不一样
        
    }
    
}*/

关于接口的实现中,我们有:

package cn.tedu.interfacex;

public class InterfaceDemo2 {
    
    public static void main(String[] args) {
        
        A a = new B1();
        
        //在 Java 中支持的是类和类之间的单继承
        //所以此时会形成一颗结构树
        //所以比较容易的就能确定两个类之间是否有继承关系
        //因此在进行强制转换的时候
        //会检查要转换的对象的声明类和转换的类型是否有继承关系
        //a对象的声明类型是 A 类,要转换的类型是B1
        //B1继承了A,所以在编译时期就不报错
        //到了运行的时候才会检查对象的实际类型和要转换的类型是否一致
        //运行的时候,发现a的实际类型是 B1,要转换的类型是B1
        //类型一致,允许转换

        B1 b1 = (B1) a;//编译、运行都不会报错
        
        //a对象的声明类型是 A 类,要转换的类型是B2
        //B2继承了A,所以在编译时期就不报错
        //到了运行的时候,a的实际类型是B1,要转换的类型是B2
        //类型不一致,所以报错 --- ClassCastException

        /*B2 b2 = (B2) a;//编译通过,但是运行会报错
        B2 b3 = (B2) b1;//编译直接不通过
        C c = (C) a;*///编译直接不通过
        //在 Java 中,类和接口之间是多实现,接口和接口之间是继承关系
        //所以构成了一张图状结构 --- 网状结构,
        //不容易确定两个节点之间的关系
        //因此在编译时期为了提高效率放弃检查
        //直到运行时候在确定类型是否是否相同
        D d = (D) a;//编译通过,但是运行报错
        
    }
}

class A{}

class B1 extends A{}

class B2 extends A{}

class C{}

interface D{}
 
接着我们谈谈关于内部类方面的知识吧!!
内部类
 
   1. 方法内部类
定义在方法中的类 --- 方法/局部内部类 --- 为了重复使用某段逻辑,并且使这段逻辑只从属于某一个方法使用
package cn.tedu.innerclass;

public class InnerDemo1 {

    public static void main(String[] args) {
        
        Outer1 outer1 = new Outer1();
        outer1.m();
    }
    
}

class Outer1{
    
    int i = 10;
    
    public void m(){
        System.out.println("Outer~~~");
        
        //当方法内部类使用所在的方法中的数据的时候,
        //要求这个属性的是一个常量
        //从JDK1.8开始,方法内部类使用到当前方法中的数据的时候,将该数据默认为常量,不能改变
        //常量的隐式声明:
        int j = 5;
        
        //方法内部类
        //只能在定义它的方法中使用
        //可以使用外部类中的属性何方法
        //如果内部类和外部类存在同名属性或者方法,则使用内部类中定义
        //只能用abstract/final修饰
        //方法内部类可以定义非静态的属性和非静态方法
        //但是不能定义静态变量和静态方法
        //然而定义静态常量
        class Inner1{//不能用static 修饰
            
            int k = 8;
            static final int x = 7;//会报错,加final变成一个常量
            
            public void m(){
                System.out.println("Inner~~~");
                i += 5;
                m2();
                //外部类.this.外部类的方法或者属性
                Outer1.this.m2();
                
                System.out.println(j);
                //j++;//会报错
                
            }
            
            public void m2(){
                System.out.println("Inner m2~~~");
            }
            
        }
        
        Inner1 i1 = new Inner1();
        i1.m();
    }
    
    public void m2(){
        System.out.println("m2~~~");
    }
    
}
    2. 成员内部类
Outer2.Inner2 oi2 = new Outer2().new Inner2();
 
package cn.tedu.innerclass;

public class InnerDemo2 {
    
    public static void main(String[] args) {
        
        Outer2 o2 = new Outer2();
        
        Outer2.Inner2 oi2 = new Outer2().new Inner2();//利用外部类对象创建内部类
        System.out.println(oi2.j);
    }
    
}

class Outer2{
    
    int i = 3;
    
    Inner2 i2 = new Inner2();
    
    //成员内部类
    //可以使用外部类中的属性和方法
    //可以定义非静态属性和非静态方法
    //但是不能定义静态属性和静态方法
    //然而定义静态常量
    class Inner2{//修饰不受什么限制
        
        int j = 10;
        static final int k = 8;//也是要加final
        
        public void m(){
            i += 3;
            Outer2.this.m();//调用外部类中的,不然就是递归了
        }
        
    }
    
    public void m(){
        System.out.println("Outer~~~");
    }
    
}
    3. 静态内部类
用 static 修饰的类
Outer3.Inner3 oi3 = new Outer3.Inner3();
package cn.tedu.innerclass;

public class InnerDemo3 {

    public static void main(String[] args) {
        
        Outer3.Inner3 oi3 = new Outer3.Inner3();
        oi3.m();
        
    }
    
}

class Outer3{
    
    int i = 0;
    
    //静态内部类
    //只能使用外部类中的静态属性和静态方法
    //静态内部类中可以定义一切的方法和属性,无论静态还是非静态
    static class Inner3{
        
        
        int j = 8;
        static int k = 4;
        
        public void m(){
            //System.out.println(i);//报错
        }
        
    }
    
}
    4. 匿名内部类
匿名内部类本质上是实现了对应的接口或者是继承了对应的类
任何一个接口都可以存内在匿名内部类形式 
一个类只要可以被继承,那么就可以存在匿名内部类形式 --- 最终类不存在匿名内部类的形式
package cn.tedu.innerclass;

public class InnerDeom4 {

    public static void main(String[] args) {
        
        //匿名内部类
        //a是匿名内部类产生的对象
        //匿名内部类实际上是实现了对应的接口
        A a = new A(){

            @Override
            public void m() {
                System.out.println("running");
            }
            
        };
        
        a.m();
        //匿名内部类实际上是继承了对应的类
        B b = new B(){

            @Override
            public void m() {
                System.out.println("running");
            }};
            b.m();
            
            //C c = new C(){};
    }
    
}

interface A{
    
    void m();
    
}

abstract class B{
    
    public abstract void m();
}

final class C{}
 
扩展:类中可以定义类,类中也可以定义接口,接口中也可以定义类,接口中也可以定义接口 --- 如果类中定义了接口或者式接口中定义了接口,那么称之为内部接口 --- 类中定义的接口,以及接口中定义的类和接口默认都是静态的。
 
谈到这儿,还有个 Lambda 表达式的内容:
package cn.tedu.innerclass;

public class LambdaDemo {

    public static void main(String[] args) {
        
        //接口中只定义了1个抽象方法
        //可以利用
        /*Calc c = new Calc(){

            @Override
            public double add(double i, double j) {
                // TODO Auto-generated method stub
                return i + j;
            }};*/
        //表示重写Calc中的唯一的一个抽象方法add
        //Lambda表达式只能作用在函数式接口上
        //方法体只有一句,可以省略{}和return 不写
        //唯一的依据方法体的计算结果默认为当前方法的返回值
        
        //Calc c = (double i, double j) -> i + j;
        
        //重写的是 Calc 接口中的方法add
        //add方法的参数列表的类型是已知的
        //可以省略参数类型不写
        //函数式编程
        Calc c = (x, y) -> x + y;
        
        System.out.println(c.add(4.3, 2.5));
    }
    
}

interface Calc{
    
    double add(double i, double j);
}

接着,前面打一个小结,接下来进行包的介绍:

声明包用的是 package --- 区分同名类,进行功能的划分
导入包用的是 import --- 导包的作用是用于提示代码从哪儿去找这个类(比如,我们前段时间经常使用的 Scanner、Arrays)
这个类
* 表示导入当前包下的所有的类但是不包括子包下的类
java --- 原生包,提供的一些常用的包,sun 公司自己用的
javax --- 扩展包,后阶段用的比较多
org --- 第三方厂商提供的一些常用的包,sun公司感觉比较好就收过来了
 
java.lang - 核心/基本包( System  String)  ,包含了 Java 程序运行需要的基本类,在 Java 程序启动的时候,包下的类就已经自动加载到内存中,所以使用的时候可以不用导包
java.awt
java.applet --- JDK 1.9 后大部分类已经被抛弃了
java.util - 工具包
java.math --- 数学运算
java.io ---数据传输
java.net --- 网络通信,后面介绍
java.nio --- 高并发,服务器有关的包,双十一的时候抢购处理这些数据
java.text --- 格式化,超市去买东西,结账系统,8.88,* 3 * 0.88 出现 4 位小数,现实生活中是两位,就用这个包
 
总结:java.lang 包下的类以及同包类在使用的时候可以不用导包
 
包就是一个用来声明和导入的,现阶段还没有什么更多的知识介绍,以后遇到了,再介绍吧!!接下来谈谈垃圾分代回收方面的知识:
 
垃圾分代回收机制
(针对的是堆内存。)
java 中的每种数据类型大小都是确定的,所以所有的内存是由 Java 自己进行分配,意味着内存的管理和回收也是由 JVM 自己进行 --- 在 Java 中一旦产生内存问题导致程序员无法处理。(在 C 和 C++ 中就是自己来定义管理的)理论上在正常情况下 Java 中的堆内存是足够使用的 --- 当堆内存使用的负荷量(堆内存的 70 %,可能有的时候又不一样)超过一定限度的时候,会启动垃圾回收器(Garbage Collector --- GC)进行堆内存的回收释放 --- 
C int 只要不超过 4 个字节就行。
int i = 3;   --- 3
int j = i;   --- 1
 
  
扩展:eden:from :to = 8:1:1
上图的简介:对象刚创建的时候是先放入新生代中的伊甸园区;如果在伊甸园区经过一次回收依然存在,那么将这个对象挪到幸存区,在幸存区中经过多次回收这个对象依然存在则挪到老生代。在回收的时候先回收新生代,如果新生代回收之后的内存足够使用则不扫描老生代。老生代的扫描频率要低于新生代
发生在新生代的回收 --- 初代回收 minor gc
发生在老生代的回收 --- 完全回收 full gc
(对于双十一,购物完之后就产生和销毁,应该将新生代设置得大一点,方便对象的创建)
扩展:对象创建完成之后会先试图放入新生代;如果新生代经过回收之后也放不开,则直接试图将该对象放入老生代。老生代如果也放不开,则会出现错误 --- OutOfMemoryError.
原文地址:https://www.cnblogs.com/tangdiao/p/9470431.html