zoukankan      html  css  js  c++  java
  • 3、java面向对象编程

    1、面向对象内存分析

    • 栈的特点

        (1)JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)

        (2)栈属于线程私有,不能实现线程间的共享!

        (3)栈的存储特性是:先进后出,后进先出。由系统自动分配,速度快!是一个连续的内存空间。

        (4)栈描述的是方法执行的内存模型,每个方法被调用会创建一个栈帧(存储局部变量、操作数、方法出口等),方法结束,栈帧消失。

    • 堆的特点:

        (1)JVM只有一个堆,被所有的线程共享。

        (2)堆是一个不连续的内存空间,分配灵活,速度慢!

        (3)堆用于存储好的对象和数组(数组也是对象)

    • 方法区(又叫静态区):它在堆里。

        (1)JVM只有一个方法区,被所有的线程共享.

        (2)用于存放程序里唯一的内容(类对象,静态变量、字符串常量等)

    • 举例

        (1)代码

     1 public class SxtStu {
     2     
     3     //属性fields
     4     int  id;
     5     String  sname;
     6     int  age;
     7     
     8     Computer  comp;  //引用数据类型
     9     
    10     //方法
    11     void  study(){
    12         System.out.println("电脑名称是:"+comp.brand);
    13     }
    14     
    15     void  play(){
    16         System.out.println("我在玩游戏!哈哈哈"); 
    17     }
    18     
    19     //构造方法。用于创建这个类的对象。无参的构造方法可以由系统自动创建。
    20     SxtStu(){
    21         System.out.println("调用了无参的构造方法!");
    22     }
    23     
    24     //程序执行的入口,必须要有
    25     //javac  Sxtstu.java   ,   java Sxtstu
    26     public static void main(String[] args) {
    27         SxtStu  stu = new SxtStu();   //创建一个对象
    28         stu.id=1001;
    29         stu.sname= "小明";
    30         stu.age = 18;
    31         
    32         Computer  c1 = new Computer();
    33         c1.brand = "联想";
    34         
    35         stu.comp = c1;
    36         
    37         stu.play();
    38         stu.study();
    39         
    40     }
    41 }
    42 
    43 class  Computer {
    44     String  brand;
    45 }

        (2)画图说明程序运行中内存变化

         

     2、构造器-->构造方法

    • 基本要点

        (1)通过new关键字调用

        (2)构造器虽然有返回值,但是不能定义返回值类型(返回值类型肯定是本类),不能在构造器里使用return关键字。

        (3)如果没有定义构造器,编译器会自动定义一个无参的构造函数。

        (4)构造器的方法名必须和类名一致。

    • 代码实例

    public class BallGame {
    
        double x;
        double y;
    
        // 重载构造方法
        public BallGame(double x,double y ){
            this.x=x;
            this.y=y;
        }
        public void jiaGame(){
            System.out.println(x+y);
        }
    
        public static void main(String[] args) {
            BallGame ballGame = new BallGame(12,30);
            ballGame.jiaGame();
        }
    }

    3、 this以及static关键字

    • 创建对象分为四步:

        (1)分配对象空间,并将对象成员变量初始化为0或者null

        (2)执行属性值的显示初始化--->赋值

        (3)执行构造方法

        (4)返回对象的地址给相关的变量

    • this关键字:本质是创建好的对象的地址!由于构造方法调用前,对象已经创建。因此构造方法可以用this代表当前对象。
    • this常用的用法:

        (1)变量产生歧义!在普通方法里,this指向调用该方法的对象。构造方法里,this指向初始化的对象。

        (2)用于重载构造方法,避免相同的初始化代码。但只能在构造方法里用,并且必须位于构造方法的第一句。

        (3)不能用于static修饰的方法里。

    • static关键字:用static声明的成员变量为静态成员变量,也称为类变量。

        (1)为类的共享变量,在类被载入时被显示初始化。

        (2)一般用类名.类属性/方法 来调用。

        (3)在static方法里不可直接访问非static的成员

    4、垃圾回收机制

    • 内存管理

        (1)java的内存管理很大程度就是对象的管理,包括对象空间的分配和释放。

        (2)对象空间的分配:使用new关键字创建对象即可

        (3)对象空间的释放:将对象赋值null即可。垃圾回收器将负责回收所用“不可达”对象的内存空间

    • 垃圾回收过程

        (1)发现无用的对象---->没有变量引用该对象。

        (2)回收无用对象占用的内存空间。

    • 垃圾回收算法。

        (1)引用计数法

        (2)引用可达法

    • 分代垃圾回收机制-->不同生命周期的对象可以采取不同的回收算法,提高回收效率。对象有三种状态:年轻代,年老代,持久代。

        (1)年起代:新产生的对象首先放在Eden区。目标是尽可能快速的收集那些生命周期短的对象即Minor GC.每次Minor GC会清理年轻代的内存算法采用效率高的复制算法。但频繁的操作会浪费内存空间。单年轻代堆满对象后,将未失效的对象放入年老代区域。

        (2)年老代:在年轻代里经历了N(默认是15次)垃圾回收后仍然存活的对象,会被放入老年代里。当年老代对象很多的时候,会启动Major GC和Full GC(全量回收),来一次大扫除。全面清理年轻代和老年代的对象。但这样对系统内存压力大。

        (3)持久代:存放静态文件。

                  

                    

         (4)导致Full GC可能原因:

            1、年老代被写满

            2、持久代被写满

            3、System.gc()被显示调用(程序建议GC启动,不是调用GC)

             4、上一次GC之后Heap的个区域分配策略动态变化

    • 容易导致内存泄漏的操作

         (1)创建大量无用对象

         (2)静态集合类使用--->HashMap、Vector、List使用容易出现内存泄漏,这些静态变量的生命周期和应用程序一致。所有的Object不能被释放。

         (3)各种连接对象(IO流、数据库连接对象、网络连接对象)未关闭

        (4)释放对象时,没有删除相应的监听器。

          (5)要点:程序员无权调用垃圾回收器;可以使用System.gc().该方法只是通知JVM,并不是运行垃圾回收器。尽量少用,会启动Full GC;finalize方法是提供程序员释放对象的方法,但尽量少用。

    5、package与import

    • 静态初始化块

        (1)如果希望加载后,对整个类进行某些初始化操作,可以使用static初始化块。类第一次载入时先执行static代码块并且只执行一次。

        (2)在类初始化时执行,不是在创建对象时执行。静态初始化块里不能访问非static成员。

        (3)执行顺序:先执行Object静态初始化块,再向下执行子类的静态初始化块。构造方法执行顺序也是这样。

    • package

                  (1)作用:
                            为了解决类之间的重名问题。
                            为了便于管理类:合适的类位于合适的包!
                 (2)怎么用?
                            通常是类的第一句非注释性语句。
                            包名:域名倒着写即可,再加上模块名,并与内部管理类。
                 (3)注意事项:
                           写项目时都要加包,不要使用默认包。
                           com.gao和com.gao.car,这两个包没有包含关系,是两个完全独立的包。只是逻辑上看起来后者是前者的一部分。

        (4)JDK主要包:

          • java.lang:包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
          • java.awt:包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
          • java.net:包含执行与网络相关的操作的类。
          • java.io: 包含能提供多种输入/输出功能的类。
          • java.util:包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。

    • import

        (1)为什么需要import?
            • 如果不适用import,我们如果用到其他包的类时,只能这么写:java.util.Date,代码量太大,不利于编写和维护。通过import可以导入其他包下面的类,从而可以在本类中直接通过类名来调用。
        (2)import怎么使用?
            • import java.util.Date;
            • import java.util.*; //导入该包下所有的类。会降低编译速度,但不会降低运行速度。
        (3)注意要点:
            •java会默认导入java.lang包下所有的类,因此这些类我们可以直接使用。
            • 如果导入两个同名的类,只能用包名+类名来显示调用相关类: java.util.Date date = new java.util.Date();
          (4)import static:用于导入指定类的静态属性, JDK5.0后增加!   

                              如何使用:
                                 • import static java.lang.Math.*;//导入Math类的所有静态属性
                                 • import static java.lang.Math.PI;//导入Math类的PI属性
            • 然后,我们可以在程序中直接使用:System.out.println(PI);

         (5)final关键字的作用

            • 修饰变量:一旦赋值,就不能重新赋值了。 final int A = 120;

            • 修饰方法:该方法不可被子类重写,但可以被重载。final void study(){}

            • 修饰类:不能被继承。final class A {}

     6、参数传递机制

        java里,方法里所有参数都是“值传递”,也就是传递的是值的副本。不会改变原参数。

    • 基本数据类型传值:传递的是值的副本,副本改变不会影响原参数。
    • 引用类型参数传值:传递的值对象的内存地址。副本和园参数指向了同一个地址。改变副本指向对象的值,原参数的值也改变。

    7、面向对象三大特性

    • 继承

        (1)类是对对象的抽象,继承是对某一批类的抽象,从而实现对现实世界更好的建模。提高代码的复用性!继承用extands关键字。子类是父类的扩展.不同的叫法:超类、父类、基类、子类、派生类

     1 public class TestExtends {
     2 public static void main(String[] args) {
     3 Mammal m1 = new Mammal();
     4 m1.puru();
     5 m1.eat();
     6 }
     7 }
     8 class Animal {
     9 String eyes=" 眼睛";
    10 String name=" 无名";
    11 public void eat(){
    12 System.out.println(" 动物吃东西!");
    13 }
    14 }
    15 class Mammal extends Animal {
    16 // 哺乳
    17 public void puru(){
    18 System.out.println(" 小动物吃奶!");
    19 }
    20 }

        (2)子类继承父类的成员变量和成员方法,但不继承父类的构造方法.类加载时先加载父类的构造方法。

        (3) java中只有单继承, java中的多继承,可以通过接口来实现。

        (4) 如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object。

        (5)instanceof是二元运算符,左边是对象,右边是类;当对象是右边类或者子类所创建的对象时,返回true。反之返回false。

        (6)在子类中可以根据需要对从基类中继承来的方法进行重写。重写方法必须和被重写方法具有相同方法名称、参数列表和返回类型。重写方法的返回值类型小于等于被重写方法。

                             • 父类方法的重写:
                                        “==”:方法名、形参列表相同。
                                         “≤≤”:返回值类型和异常类型,子类小于等于父类。
                                         “≥”:访问权限,子类大于等于父类
                            • 构造方法调用顺序:
                                        根据super的说明,构造方法第一句 总是:super(…)来调用父类对应的构造方法。
                                        先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。

    public class TestOverride {
    public static void main(String[] args ) {
    Animal animal = new Animal();
    animal.shout();
    Dog dog = new Dog();
    dog.shout();
    }
    }
    class Animal{
    void shout(){
    System. out.println("发出声音!");
    }
    }
    class Dog extends Animal {
    void shout(){
    System. out.println("旺旺旺!");
    }
    }

        (7)对象的比较==和equals()

            • ==:
              • 比较两基本类型变量的值是否相等
              • 比较两个引用类型的值即内存地址是否相等,即是否指向同一对象。
            • equals() :
              • 两对象的内容是否一致
            • 示例
              • object1.equals(object2) 如:p1.equals(p2)
              • 比较所指对象的内容是否一样
              • 是比较两个对象,而非两个基本数据类型的变量
            • object1 == object2 如:p1==p2
              • 比较p1和p2的值即内存地址是否相等,即是否是指向同一对象。
            • 自定义类须重写equals(),否则其对象比较结果总是false。

           (8)super是直接父类对象的引用,可以通过super来访问父类中被子类覆盖的方法或属性。

    • 封装---->一般在javabeen里将属性和方法封装好。

        (1)隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。我们程序设计要追求“高内聚,低耦合”。高内聚 :就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合 :仅暴露少量的方法给外部使用。

        (2)封装要点:

          • 类的属性的处理:一般使用private. (除非本属性确定会让子类继承),提供相应的get/set方法来访问相关属性. 这些方法通常是public,从而提供对属性的读取操作。(注意:boolean变量的get方法是用:is开头!)
          • 一些只用于本类的辅助性方法可以用private, 希望其他类调用的方法用public

        (3)访问控制符:

          • 成员(成员变量或成员方法)访问权限共有四种:
            • public 公共的:可以被项目中所有的类访问。(项目可见性)
            • protected 受保护的:可以被这个类本身访问;同一个包中的所有其他的类访问;被它的子类(同一个包以及不同包中的子类)访问
            • default/friendly 默认的/友好的(包可见性):被这个类本身访问;被同一个包中的类访问。
            • private 私有的:只能被这个类本身访问。(类可见性)
          • 类的访问权限只有两种
            • public 公共的:可被同一项目中所有的类访问。 (必须与文件名同名)
            • default/friendly 默认的/友好的:可被同一个包中的类访问。

    • 多态

        (1)多态时方法的多态,不是属性的多态(多态和属性无关)

        (2)多态存在有3个必要条件:继承,方法重写,父类引用指向子类对象。

        (3)父类引用指向子类对象后,用该父类引用调用子类重写的方法时,就是多态了

    package object;
    public class TestPolym {
    public static void main(String[] args) {
    Animal animal = new Dog(); //向上可以自动转型
    System.out.println(animal.age); //属性调用时,仍然是基类的属
    性。属性没有多态!
    // animal.shout();
    animalCry(new Dog());
    //传的具体是哪一个类就调用哪一个类的方法。大大提高了程序的可扩展性。
    //如果没有多态,我们这里需要写很多重载的方法。如果增加一种动物,就需
    要重载一种动物的喊叫方法。非常麻烦。
    //有了多态,只需要增加这个类继承Animal基类就可以了。
    animalCry(new Cat());
    Dog dog = (Dog) animal; //编写程序时,如果想调用运行时类型的方
    法,只能进行类型转换。不然通不过编译器的检查。
    dog.gnawBone();
    System.out.println(dog instanceof
    Animal);
    System.out.println(animal instanceof Cat);
    System.out.println(animal instanceof Dog);
    }
    static void animalCry(Animal a){
    a.shout();
    }
    }
    class Animal {
    int age=10;
    public void shout(){
    System.out.println("叫了一声!");
    }
    }
    class Dog extends Animal {
    int age=28;
    public void shout() {
    System.out.println("旺旺旺!");
    }
    public void gnawBone(){
    System.out.println(“我在啃骨头");
    }
    }
    class Cat extends Animal {
    int age=18;
    public void shout() {
    System.out.println("喵喵喵喵!");
    }
    }
    • 对象的转型

        (1)子类转换为父类:自动转换.即父类引用指向子类对象。

          • 上转型对象不能操作子类新增的成员变量和方法。
          • 上转型对象可以操作子类继承或重写的成员变量和方法
          • 如果子类重写了父类的某个方法,上转型对象调用该方法时,是调用的重写方法。
        (2)父类转换为子类:强制转换
          • (绝不是做手术,而是父类的真面目就是一个子类,否则会出现类型转换错误)

     8、抽象类

        (1)是一种模版模式。抽象类为所有子类提供了一个通用模版,子类可以在这个模版基础上进行扩展。通过抽象类,可以避免子类设计的随意性。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
        (2)要点:

            • 抽象方法和抽象类均必须用abstract来修饰。
            • 抽象方法没有方法体,只需要声明不需实现。
            • 有抽象方法的类只能定义能抽象类
            • 相反抽象类里面的方法不一定全是抽象方法,也可能没有抽象方法。
            • 抽象类可以包含属性、方法、构造方法。
            • 抽象类不能实例化,及不能用new来实例化抽象类,只能用来被子类调用。
            • 抽象类只能用来继承。
            • 抽象方法必须被子类实现。抽象类的子类必须覆盖所有的抽象方法才能被实例化,否则还是抽象类
        (3)实例

    1 abstract class Animal {
    2 abstract void shout(); //抽象方法没有方法体!
    3 }
    4 class Dog extends Animal {
    5 void shout() { //必须重写父类的抽象方法否则编译通不过
    6 // TODO Auto-generated method stub
    7 System.out.println("旺旺旺!");
    8 }
    9 }

    9、接口

        (1) 为什么需要接口?接口和抽象类的区别?

          • 接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。
          • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是
    坏人,则必须欺负好人。
          • 接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
          • 项目的具体需求是多变的,我们必须以不变应万变才能从容开发,此处的“不变”就是“规范”。因此,我们开发项目往往都是面向接口编程!
        (2)接口相关规则

          • 接口中所有方法都是抽象的。
          • 即使没有显式的将接口中的成员用public标示,也是public访问类型的
          • 接口中变量默认用 public static final标示,所以接口中定义的变量就是全局静态常量。
          • 可以定义一个新接口,用extends去继承一个已有的接口
          • 可以定义一个类,用implements去实现一个接口中所有方法。
          • 可以定义一个抽象类,用implements去实现一个接口中部分方法

        (3)如何实现接口

          • 子类通过implements来实现接口中的规范
          • 接口不能创建实例,但是可用于声明引用变量类型。
          • 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
          • Java的类只支持单继承,接口支持多继承

          • Java中,一个类只能继承一个类,但同时可以实现多个接口,既可以实现多重继承的效果和功能,也避免的多重继承的危险性。如:class Student extents Person implements Runner,Flyer

    10、内部类

        (1)定义:将一个类定义置入另一个类定义中就叫作“内部类”。

          • 内部类作为外部类的成员,可以直接访问外部类的成员(包括private成员),反之则不行。
          • 内部类做为外部类成员,可声明为private、默认、protected或public。
          • 内部类成员只有在内部类的范围之内是有效的。
          • 用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。
          • 编译后生成两个类: OuterClass.class 和OuterClass$InnerClass.class

        (2)内部类分类:成员内部类 ;静态内部类 ;方法内部类 ;匿名内部类

     1 class Outer{
     2 int outer_i = 100;
     3 void test(){
     4 Inner in = new Inner();
     5 in.display();
     6 System.out.println(in.a);
     7 }
     8 class Inner{
     9 int a=5;
    10 void display(){
    11 System.out.println("display: outer_i = " + outer_i);
    12 }
    13 }
    14 }

     11、String类

    • 基础:

        (1)String类又称不可变序列。

        (2)String位于java.lang里,java默认导入java.lang下所有类。

        (3)Java字符串就是Unicode字符序列。例如字符串“Java”就是四个Unicode字符‘J’,‘a’,'v','a'组成的。

        (4)每个用双引号括起来的字符串就是String类的一个实例

    • 常量池

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

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

        (3)运行时常量:在类加载完成后,每个class常量池里的符号引用值转存到运行时常量池。

  • 相关阅读:
    JVM基础和调优(一)
    Jtree(节点的渲染+资源管理器)(2)
    Jtree (节点的渲染+资源管理器)
    jtree(选择框)
    java进阶计划
    SQL优化
    spring 事务管理
    Spring事务配置的五种方式(转)
    spring与数据库之间的配置
    struct2(六) 为表单添加验证
  • 原文地址:https://www.cnblogs.com/mlyflow/p/10664502.html
Copyright © 2011-2022 走看看