JAVA学习笔记(面向对象)

面向对象三大特征

封装性、继承性、多态性

1.对象说白了也是一种数据结构(对数据的管理模式),将数据和数据的行为放到了一起。
2.在内存上,对象就是一个内存块,存放了相关的数据集合。
3.对象的本质就一种数据的组织方式。
4.对象是具体的事物;类是对对象的抽象;
5…类可以看成一类对象的模板,对象可以看成该类的一个具体实例。
6…类是用于描述同一类型的对象的一个抽象概念,类中定义了这一类对象所应具有的共同的属性、方法。

类的定义方法

每一个源文件必须有且只有一个public class,并且类名和文件名保持一致!
类中包含属性、方法及构造器。

public class SxtStu {
    //属性(成员变量)
    int id;
    String sname;
    int age;  
    //方法
    void study(){
        System.out.println("我正在学习!");
    }  
    //构造方法
   SxtStu(){
   }
   //其他类
   class Group{
   }

包对于类,相当于文件夹对于文件的作用。可以有效解决类重名的问题。
1.Java会默认导入java.lang包下所有的类,因此这些类我们可以直接使用。
2. 如果导入两个同名的类,只能用包名+类名来显示调用相关类:
java.util.Date date = new java.util.Date();
import static java.lang.Math.*;//导入Math类的所有静态属性
import static java.lang.Math.PI;//导入Math类的PI属性

继承

1.父类也称作超类、基类、派生类等。
2.Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。
3.Java中类没有多继承,接口有多继承。
4.子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
5.如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object【因为Object类是所有JAVA类的根基类】。

instance of 运算符
instanceof是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回true;否则,返回false。

方法的重写override
1.方法名、形参列表相同。
2.返回值类型和声明异常类型,子类小于等于父类。
3.访问权限,子类大于等于父类。

比较方法
==”代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。
equal比较两个对象的hashcode,是同一个对象的引用时返回 true 否则返回 false。(可根据要求重写)
使用:Eclipse:右键->源码

封装

属性设置

  1. private 表示私有,只有自己类能访问
  2. default表示没有修饰符修饰,只有同一个包的类能访问
  3. protected表示可以被同一个包的类以及其他包中的子类访问
  4. public表示可以被该项目的所有包中的所有类访问
    在这里插入图片描述
    属性处理
  5. 一般使用private访问权限。
  6. 提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是is开头!)。
  7. 一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
    内部类
    一般情况,我们把类定义成独立的单元。有些情况下,我们把一个类放在另一个类的内部定义,称为内部类(innerclasses)。
    作用:
  8. 内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
  9. 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。
  10. 接口只是解决了多重继承的部分问题,而内部类使得多重继承的解决方案变得更加完整。
  11. 由于内部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所以,在只为外部类提供服务的情况下可以优先考虑使用内部类。
  12. 使用内部类间接实现多继承:每个内部类都能独立地继承一个类或者实现某些接口,所以无论外部类是否已经继承了某个类或者实现了某些接口,对于内部类没有任何影响。

分类:成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类。
成员内部类
i. 非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
ii. 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
iii. 非静态内部类不能有静态方法、静态属性和静态初始化块。
iv. 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
访问要点:

  1. 内部类里方法的局部变量:变量名。
    2.内部类属性:this.变量名。
  2. 外部类属性:外部类名.this.变量名。
//非静态内部类
class Outer {
    private int age = 10;
    class Inner {
        int age = 20;
        public void show() {
            int age = 30;
            System.out.println("内部类方法里的局部变量age:" + age);// 30
            System.out.println("内部类的成员变量age:" + this.age);// 20
            System.out.println("外部类的成员变量age:" + Outer.this.age);// 10
        }
    }
}
public class TestInnerClass {
    public static void main(String[] args) {
        //先创建外部类实例,然后使用该外部类实例创建内部类实例
        Outer.Inner inner = new Outer().new Inner();
        inner.show();
        Outer outer = new Outer();
        Outer.Inner inn = outer.new Inner();
        inn.show();
    }
}

//静态内部类
class Outer{
    //相当于外部类的一个静态成员
    static class Inner{
    }
}
public class TestStaticInnerClass {
    public static void main(String[] args) {
        //通过 new 外部类名.内部类名() 来创建内部类对象
        Outer.Inner inner =new Outer.Inner();
    }
}

匿名内部类
适合那种只需要使用一次的类。比如:键盘监听操作等等。
#无访问修饰符及构造方法

//语法
new  父类构造器(实参类表) \实现接口 () {
           //匿名内部类类体!
}
//实例

this.addWindowListener(new WindowAdapter(){
        @Override
        public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
    }
);
this.addKeyListener(new KeyAdapter(){
        @Override
        public void keyPressed(KeyEvent e) {
            myTank.keyPressed(e);
        }      
        @Override
        public void keyReleased(KeyEvent e) {
            myTank.keyReleased(e);
        }
    }
);

局部内部类
实际开发中使用很少。

多态

多态指的是同一个方法调用,由于对象不同可能会有不同的行为。
多态的要点

  1. 多态是方法的多态,不是属性的多态(多态与属性无关)。
  2. 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。
  3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。

对象的转型
父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。
向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型。

public class TestCasting {
    public static void main(String[] args) {
        Object obj = new String("北京尚学堂"); // 向上可以自动转型
        // obj.charAt(0) 无法调用。编译器认为obj是Object类型而不是String类型
        /* 编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换。
         * 不然通不过编译器的检查。 */
        String str = (String) obj; // 向下转型
        System.out.println(str.charAt(0)); // 位于0索引位置的字符
        System.out.println(obj == str); // true.他们俩运行时是同一个对象
    }
}

抽象方法和抽象类

抽象方法
使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。

抽象类
包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
要点:

  1. 有抽象方法的类只能定义成抽象类
  2. 抽象类不能实例化,即不能用new来实例化抽象类。
  3. 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用。
  4. 抽象类只能用来被继承。
  5. 抽象方法必须被子类实现。
//抽象类
abstract class Animal {
    abstract public void shout();  //抽象方法
}

接口

接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。
抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。

[访问修饰符]  interface 接口名   [extends  父接口1,父接口2]  {
常量定义;  
方法定义;
}

定义

  1. 访问修饰符:只能是public或默认。
  2. 接口名:和类名采用相同命名机制。
  3. extends:接口可以多继承。
  4. 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
  5. 方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract。
  6. 子类通过implements来实现接口中的规范。
  7. 接口不能创建实例,但是可用于声明引用变量类型。
    8.一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
  8. 接口完全支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所定义的一切。
/**飞行接口*/
interface Volant { 
    int FLY_HIGHT = 100;  // 总是:public static final类型的;
    void fly();   //总是:public abstract void fly();
}
/**善良接口*/
interface Honest { 
    void helpOther();
}
/**Angle类实现飞行接口和善良接口*/
class Angel implements Volant, Honest{
    public void fly() {
        System.out.println("我是天使,飞起来啦!");
    }
    public void helpOther() {
        System.out.println("扶老奶奶过马路!");
    }
}
/**继承接口**/
interface Test extends Volant,Honest{
}

关键字

this关键字
this的本质就是“创建好的对象的地址”
#1. 在程序中产生二义性之处,应使用this来指明当前对象;普通方法中,this总是指向调用该方法的对象。构造方法中,this总是指向正要初始化的对象。
2. 使用this关键字调用重载的构造方法,避免相同的初始化代码。但只能在构造方法中用,并且必须位于构造方法的第一句。
3. this不能用于static方法中。

static关键字
4. 为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化。
5. 对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享!!
6. 一般用“类名.类属性/方法”来调用。(也可以通过对象引用或类名(不需要实例化)访问静态成员。)
7. 在static方法中不可直接访问非static的成员。
#static修饰的成员变量和方法,从属于类。
#普通变量和方法从属于对象的。
#静态初始块执行顺序与构造方法同。

super关键字
super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。
使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
若是构造方法的第一行代码没有显式的调用super(…)或者this(…);那么Java默认都会调用super(),含义是调用父类的无参数构造方法。这里的super()可以省略。
final关键字

  1. 修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
    2.修饰方法:该方法不可被子类重写。但是可以被重载!
  2. 修饰类: 修饰的类不能被继承。比如:Math、String等。

常量池

1. 全局字符串常量池(String Pool)
全局字符串常量池中存放的内容是在类加载完成后存到String Pool中的,在每个VM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)。

2. class文件常量池(Class Constant Pool)
class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量(文本字符串、final常量等)和符号引用。

3. 运行时常量池(Runtime Constant Pool)
运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。

String

当"+"运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接。

内存分析

一、内存分析
JAVA虚拟机内存可分为三个区域:栈stack、堆heap、方法区method area
栈的特点如下:
1.栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
2. JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
3.栈属于线程私有, 不能实现线程间的共享!
4.栈的存储特性是 "先进后出,后进先出”
5.栈是由系统自动分配,速度快,栈是一个连续的内存空间。

堆的特点如下:

  1. 堆用于存储创建好的对象和数组(数组也是对象)
  2. JVM只有一个堆,被所有线程共享
  3. 堆是一个不连续的内存空间,分配灵活,速度慢!

方法区(又叫静态区)特点如下:

  1. JVM只有一个方法区,被所有线程共享!
  2. 方法区实际也是堆,只是用于存储类、常量相关的信息!
  3. 用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】、静态变量、字符串常量等)

二、垃圾回收原理

内存管理
Java的内存管理很大程度指的就是对象的管理,其中包括对象空间的分配和释放。
对象空间的分配:使用new关键字创建对象即可
对象空间的释放:将对象赋值null即可。垃圾回收器将负责回收所有”不可达”对象的内存空间。

垃圾回收过程
任何一种垃圾回收算法一般要做两件基本事情:

  1. 发现无用的对象
  2. 回收无用对象占用的内存空间。
    垃圾回收机制保证可以将“无用的对象”进行回收。无用的对象指的就是没有任何变量引用该对象。Java的垃圾回收器通过相关算法发现无用对象,并进行清除和整理。

三、分类回收机制
不同的对象的生命周期是不一样的,故不同生命周期的对象采用不同的回收算法,。JVM将堆内存划分为三个区。
年轻代->Eden区
尽可能快速地收集掉生命周期短的对象,对应Minor GC算法采用效率较高的复制算法,频繁操作,但会浪费内存空间。当趋于放满对象后,就对象存放到年老代区域。
年老代->Survivo区
在年轻代中经历了N(默认15)次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。当存放趋满时,启动Major GC和Full GC(全量回收),来一次大扫除,全面清理年轻代区域和年老代区域。
持久代->Tenured/Old区
用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响。

四、 容易造成内存泄露的场景
1.创建大量无用对象
2.静态集合类的使用
3. 各种连接对象(IO流对象、数据库连接对象、网络连接对象)未关闭
4. 监听器的使用

猜你喜欢

转载自blog.csdn.net/weixin_43761679/article/details/107079967