JavaWeb入门(二) 面向对象篇

JavaWeb入门(二) 面向对象篇

标签(空格分隔): JavaWeb



原文地址

封装 Encapsulation

把类的一些描述细节隐藏在内部,用户只能通过接口访问类中的内容,这种组织模块的方式称为封装。

class People{
    private String name = "";   
    public void setName(String _name){
        this.name = _name;
    }
    public String getName(){
        return this.name;
    }
}

继承 Inheritance

  • 为了代码复用,可以允许一个类使用另一个类的属性和方法,这样的行为称之为“继承”, 反之为“派生”

  • 复写指子类中的方法会覆盖父类的同名方法。

public class Man extends Person {

    private byte gender = 0;

    @Override
    public void run() {
        //super.run();
        System.out.println(this.getName() + getGender() + " is running");
    }

    public byte getGender() {
        return gender;
    }

    public void setGender(byte gender) {
        this.gender = gender;
    }
}

多态 Polymorphism

通俗地讲,是类的不同对象对同一个消息做出的不同的反应。其中:
静态多态:编译时的多态,即方法重载。
动态多态:运行时的多态,即方法复写与接口引用。

Cat cat = new Animal();
Dog dog = new Animal();
cat.cry();
dog.cry();

方法重载 Overload

方法重载是静态多态,是编译时的多态。指的是在同一个类里面,名称相同,但参数个数类型不同的方法。

public int add(int x, int y){}
public float add(float x, float y){}
public int add(int... value){  //int... 表示可以接收任意个数的参数,
    int temp = 0;
    for (int i = 0; i < value.length; ++i){
        temp += value[i];   //从这里可以看出value实际上是一个数组
    }
    return temp;
}

方法复写 Override

方法复写是动态多态,是运行时的多态。发生在父子类之间,子类覆盖父类的方法,子类的方法和父类的方法名称相同,参数的类型和个数也相同,返回值类型页相同,即 除了方法的具体实现不同,其他都与父类相同。


构造方法 Constructor

构造方法即实例化对象时调用的方法。
分为两种: 默认构造方法自定义构造方法
注意 : 如果类中定义了自定义的构造方法,那么一定要显式地定义默认构造方法,且默认构造方法一定要定义在最前面

public class Plane{
    public double maxSpeed;
    public Plane(){}
    public Plane(doubnle _maxSpeed){
        this.maxSpedd = _maxSpeed;
    }
}

Java修饰符

访问修饰符

可按照访问级别从高到低依次分为:
public > protected > 默认 > private

存储修饰符

static 静态存储,即在内存中固定的位置永久存在,使用static声明的类、方法、变量不会被自动释放内存,因此常用来初始化。

static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,它被类的所有实例共享。只有该类被加载, JVM就能根据类名在运行时数据区(Runtime Area) 内找到它们。因此,static对象可以在它的任何对象被创建之前被访问,无需引用任何对象。

public static修饰的成员变量和成员方法本质上是全局变量和全局方法。声明一个新对象时,不生成static对象的副本,而是所有实例共享一个static对象。

static前可以有private修饰, 表示这个变量可以在该类的静态代码块中或该类的其他静态成员方法中使用,但是却不能在其他类中通过类名直接引用,这点一定要明白,也很重要。实际上我们要搞清楚:private 是访问权限限定,static表示不需要实例化就可以引用。

static变量也称为静态变量,静态变量在内存中只有一个拷贝JVM只为它分配一次内存,在加载类的过程中完成对静态变量的内存分配,直接通过类名访问。
例: Car.MAX_SPEED
由于所有对象共享static变量,因此可以用来做一些统计工作,如统计实例个数,计算总量等等。

static代码块即静态代码块,是在类中独立于类成员的static语句块。它不在任何方法体内,JVM加载类时会执行这些代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序一次执行它们,每个代码块执行一次。

static + final 可用来修饰成员变量和成员方法,可简单理解为“全局常量”, 修饰成员变量时表示值不可修改,且通过类名访问,修饰方法时表示方法不可复写,且通过类名访问。


this与super的使用

this指对象的别名,代替当前类的实例,是当前类实例的引用,本质是指针

super则指调用父类的实例或方法。
super()表示父类的构造方法。

public class Man extends People{
    public Man(String _name){
        super(_name);   //调用父类构造方法
        System.out.println("Initialize a Man which extends People.");
    }
    public int getPeopleAge(){
        return super.getAge();  //调用父类方法
    }
}

练习: 构造1000个随机的女友并打印她们的姓名和年龄。

/**
 * Girlfriend实体类
 */
public class Girlfriend {

    private String name;
    private int age = 0;
    private String type;

    /**
     * 静态常量
     */
    public static final String TYPE_AIR_FILLED = "air_filled";
    public static final String TYPE_SILICA_GEL = "silica_gel";
    public static final String TYPE_ROBOT = "robot";

    /**
     * private类型的静态常量,无法在类外使用
     */
    private static final Map<String, String> typeMap = new HashMap<>();

    /**
     * 静态代码块
     * 每个类型对应一个用于展示的真实字符串
     */
    static {
        typeMap.put(TYPE_AIR_FILLED, "充气娃娃");
        typeMap.put(TYPE_SILICA_GEL, "实体娃娃");
        typeMap.put(TYPE_ROBOT, "机器人");
    }

    /**
     * 默认构造方法,由于存在自定义构造方法,因此要显式地声明
     */
    public Girlfriend() {
    }

    /**
     * 自定义构造方法
     *
     * @param _name
     * @param _age
     * @param _type
     */
    public Girlfriend(String _name, int _age, String _type) {
        this.name = _name;
        this.age = _age;
        this.type = _type;
    }

    /**
     * 获取类型对应的真实字符串
     *
     * @return
     */
    public String getTypeString() {
        return typeMap.get(getType());
    }

    /**
     * 复写父类Object的toString方法
     *
     * @return
     */
    @Override
    public String toString() {
        return String.format("公子,小女子名叫%s, 芳龄%d, 我是%s哦。", getName(), getAge(), getTypeString());
    }

    public String getType() {
        return type;
    }

    //...其他setter,getter省略

}

/**
 * 入口main类
**/
public class Main {

    public static void main(String[] args) {

        String[] familyNames = {"李", "宋", "周", "郑", "赵", "韦"};
        String[] lastNames = {"秀宁", "玉致", "玉珍", "芷若", "蓝秋", "敏敏", "秋香", "盈盈"};
        String[] types = {"air_filled", "silica_gel", "robot"};
        generateGirls(familyNames, lastNames, types);
    }

    private static void generateGirls(String[] familyNames, String[] lastNames, String... types) {
        for (int i = 0; i < 1000; ++i) {
            //年龄由任意姓 + 名组成
            StringBuffer fullName = new StringBuffer();
            fullName.append(familyNames[(int) (Math.random() * familyNames.length)]);
            fullName.append(lastNames[(int) (Math.random() * lastNames.length)]);

            //年龄为[18,22]之间的整数
            int age = (int)(Math.random() * 5) + 18;

            //类型为types数组中的某个
            String type = types[(int)(Math.random() * types.length)];

            //实例化一个女友
            Girlfriend girlfriend = new Girlfriend(fullName.toString(), age, type);

            //输出女友信息
            System.out.println(girlfriend.toString());
        }
    }
}

输出结果实例

公子,小女子名叫周秋香, 芳龄19, 我是实体娃娃哦。
公子,小女子名叫韦敏敏, 芳龄19, 我是实体娃娃哦。
公子,小女子名叫宋秀宁, 芳龄18, 我是机器人哦。
公子,小女子名叫李玉珍, 芳龄21, 我是机器人哦。
公子,小女子名叫郑秋香, 芳龄19, 我是充气娃娃哦。
公子,小女子名叫宋敏敏, 芳龄22, 我是机器人哦。
公子,小女子名叫郑敏敏, 芳龄19, 我是机器人哦。
公子,小女子名叫李蓝秋, 芳龄18, 我是实体娃娃哦。
公子,小女子名叫李芷若, 芳龄18, 我是充气娃娃哦。
公子,小女子名叫韦敏敏, 芳龄20, 我是充气娃娃哦。
公子,小女子名叫周秋香, 芳龄22, 我是实体娃娃哦。
公子,小女子名叫韦芷若, 芳龄18, 我是充气娃娃哦。


抽象类 Abstract Class

任何含有抽象方法的类都称为抽象类。
抽象方法是声明而未实现的方法。
抽象类与抽象方法都需要使用abstract关键字声明且抽象类必须被子类继承。

abstract public class Animal{
    private int age = 10;       //抽象类可以含有自己的属性
    public abstract void cry(); //抽象方法只声明而未实现

    //...setter & getter
}

class Cat extends Animal{
    //非抽象类继承抽象类必须复写父类的抽象方法并实现
    @Override
    public void cry(){
        System.out.println("I am Cat, I am" + getAge());
    }
}

注意:

  • 抽象类不能被实例化。
  • 若抽象类中显式地声明了构造方法,那么派生出来的子类必须实现该构造方法。

练习

打印9x9乘法表。

别看这个需求很简单,其实可以做的地方非常多。比如:

  • 将生成和打印分离,进一步对程序进行抽象,将生成乘法表的部分提取为一个方法 getMulTable()
  • 生成乘法表的过程中使用StringBuffer来处理字符串。
  • 如果用户需要保存生成的乘法表怎么办?考虑一个扩展方法saveInFile()
  • 如果用户要生成16x16的乘法表怎么办?考虑将乘法表的大小用参数的方式确定。
  • 输出样式尽量美观。
  • 如果乘法表过大比如100x100,那么样式会错位,如何解决?

示例代码

public class MulTable {

    public static void main(String... args) {

        int size = 9;
        String mulTable = getMulTable(size);
        System.out.println(mulTable);
    }

    private static String getMulTable(int _size) {
        if (_size <= 0) {
            return null;
        }

        StringBuffer result = new StringBuffer();
        for (int i = 1; i <= _size; ++i) {
            for (int j = 1; j <= i; ++j) {
                result.append(String.format("%dx%d=%d \t", j, i, i * j));
            }
            result.append("\n");
        }
        return result.toString();
    }
}

注意:
应尽量避免使用 str1 + str2 + str3的方式处理字符串,不仅效率低,而且可读性也差。


内部类

这里必须要说一嘴,内部类看似无用,实则设计的很巧妙,对于多重继承(继承自多个抽象类)等特殊场景有奇效。我编程水平有限,对内部类一知半解,用的也少,印象最深的是使用静态内部类实现懒汉式的单例模式。

例: JDBC连接工厂

public class ConnectionFactory {

    private static String driver;
    private static String dburl;
    private static String user;
    private static String password;
    private Connection connection;

    /**
     * 静态代码块用来初始化成员变量
     */
    static {
        Properties properties = new Properties();
        try {
            InputStream iStream = ConnectionFactory.class.getClassLoader().getResourceAsStream("dbconfig.properties");
            properties.load(iStream);

        } catch (Exception e) {

            e.printStackTrace();
            System.out.println("读取配置文件失败!");
        }
        driver = properties.getProperty("driver");
        dburl = properties.getProperty("dburl");
        user = properties.getProperty("user");
        password = properties.getProperty("password");
    }

    /*
     * 使用单例模式创建对象
     */
    private ConnectionFactory(){

    }
    /*
    * 这里使用静态内部类来持有单例
    */
    public static class SingletonHolder{
        private static final ConnectionFactory FACTORY = new ConnectionFactory();
    }
    public static ConnectionFactory getInstance(){
        return SingletonHolder.FACTORY;
    }

    /**
     * 创建并返回数据库连接
     */
    public Connection makeConnection(){

        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(dburl, user, password);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return connection;  
    }

    /**
     * 关闭数据库连接
     */
    public void closeConnection(){
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

接口 Interface

可以理解为一种特殊的类,里面全部由全局常量公共的抽象方法所组成。

注意:

  • 接口必须拥有子类,且子类必须要实现接口中的全部抽象方法
  • 一个子类可以实现多个接口,但是只能继承一个抽象类
  • 一个接口可以继承多个接口。
  • 一个子类可以同时继承抽象类和实现接口。
  • 接口不同于抽象类,成员不同,抽象类可以有自己的方法和属性,而接口则只有全局常量和抽象方法。这是因为二者的设计理念不同,抽象类是 is-a 关系, 而接口被设计为 like-a 的关系。

//接口声明
public interface Pet{
    String NAME = "小雪";   //等同于 public static final String NAME;
    void play();            //等同于 public abstract void play();
    void call();            //等同于 public abstract void call();
}

//实现多个接口
class Teddy implements Pet, Goods{
    //...
}

//同时继承抽象类和实现多个接口
public class Cat extends Animal implements Pet, Eat{
    //...
}

匿名内部类

若一个类在整个操作中只使用一次,就可以将其定义为匿名内部类,它是在抽象类和接口的基础上发展而来的。我在实际开发中还未用到,因此暂未体会到其设计的精妙之处。

简单的例子,仅做示范,无任何实际价值

public void tell(){
    //将匿名内部类Inter做为做为参数传给call()方法
    call(new Inter(){
        @Override
        public void print(){
            //...
        }
    });
}

数组

声明方式为:

int nums[] = null;
int[] nums = null;
//这两种方式没有区别

用以上的方式声明的数组需要分配内存:

nums = new int[1024];

一般直接在声明时分配内存:

byte[] buffer = new byte[1024];

也可以使用静态初始化的方式:

int[] array = {1, 2, 3, 4, 5};
String[] names = {"Alex", "Bob", "Cindy", "Dick", "Elfredol"};

String

字符串的处理是个很大的话题,打算用一个新专题来总结。字符串的操作有很多技巧,同时也需要一些正则表达式的知识。

posted @ 2016-12-29 11:42  庄浩  阅读(283)  评论(0编辑  收藏  举报