java进阶(面向对象编程)

  • Java程序的三个阶段
    1. Type
    2. Class
    3. Runtime
  • Java类的3个结构
    1. Field
    2. Constructor
    3. Method

黑马程序员部分

project(工程) > (src) > package(包) > class(类) > object(对象)> 成员(变量,函数和内部类)

一个类中可以有成员变量、方法,还可以有内部类。内部类实际上是它所在类的成员。内部类最重要的特点就是能够访问外部类的所有成员

面向对象编程三大特性
封装,继承,多态

Java中的变量:
1.成员变量(实例变量,属性)
2.本地变量(局部变量)
3.类变量(静态属性)

byte 字节 就是2个16进制数 00到FF 8位
1024 byte = 1k b

Java基本类型占用的字节数:
1字节: byte , boolean
2字节: char,short
4字节: int , float
8字节: long , double
附录:
编码:
数字都是一个1字节
GBK: 中文2字节
UTF-8: 中文通常3字节,在拓展B区之后的是4字节

int默认10进制,数字前面加0b表示2进制,0x表示16进制

final关键字

  • 可以用来修饰一个类,方法,局部变量,成员变量

类与对象

普通变量
对象变量(管理者)

  • 类是规范,对象是实体
  • 对象 = 属性(数据)+函数(操作) = 成员变量+成员函数
  • 封装:把数据和对数据的操作放在一起(外部的操作保护内部的数据)
  • 定义类
  • 创建对象
new VendingMachine();
VendingMachine v = new VendingMachine();

对象变量是对象的管理者
运算符.

  • 成员变量和成员函数
    • 函数要通过对象来调用
    • this是成员函数固有的本地变量,他表示调用这个函数的那个对象
    • 在成员函数内部可以直接调用自己(this)的其他成员函数
    • 定义在函数内部的是本地变量,生存期和作用域都在函数内部
    • 成员变量的生存期是对象的生存期,作用域是类内部的成员函数
  • 对象初始化
    • java会给未初始化的成员变量赋0值(广义,boolean给false,对象变量给None等)
    • 构造函数(不能有返回类型)
      • 如果有一个成员函数的名字和类的名字完全相同,构造对象时候被自动调用,这个函数则是构造函数
      • 不能有返回类型
      • 创建对象先进构造函数的头,在去定义成员变量,再进构造函数体
      • 构造函数可以有多个,只要参数表不同即可(可以接受参数,也可以不接受参数)。通过this()可以调用其他构造函数(只能使用一次)。还可以根据创建对象时给的参数来选择调用哪个构造函数。(称为重载)
      • 重载
        一个类里的同名但参数表不同的函数构成了重载关系

对象交互

类的首字母大写

  • 对象的识别
  • 对象交互
    多个对象可组成一个对象

tip(%占位符,%d整数%f小数)

  • 封闭的访问属性
    • 所有的成员都可以设定访问属性
    • private 成员为类私有,只有成员函数和成员变量可以访问
      私有是针对类而不是针对对象,即同一类的其他对象都可以访问
  • 开放的访问属性
    • public表示任何人都可以访问他
    • 没有public或者private限制的,我们称为friendly,即同一个包内的可以访问。
    • protect在继承中再提
    • class是public表示任何地方都可以用该类的定义来定义一个变量

编译单元(.java文件):一个编译单元最多只能有一个class是public

  • 包(管理机制)

    • 在一个目录底下的所有编译单元(源代码文件),包的名字就是目录的名字。
    • 如果不import其他包,想要用里面的类就得给全名,比如display.Display。import display.*不推荐,防止和本地包的类重名
    • 包的里面还可以包含包,如java.util.Scanner
  • 类变量

public class Display{
   private static int step = 1;
}
- d1.step = d2.step = Display.step
- 可通过对象访问类变量,但是类变量不属于对象

- 类函数(static函数)
属于类的函数,可以访问其他类函数

public class Display{
   public static viod main = {};
}

static成员的初始化和对象的创建没关系

对象容器

  • 泛型容器类

    • 容器的两个类型
      1. 容器的类型(ArrayList)
      2. 元素的类型(String)
    • ArrayList < String > (ArrayList of String)
      • 创建一个该类对象notes后可用的操作
        • notes.size() 查看有多少条
        • notes.get(index) 获得相应索引的元素
        • notes.add(location,element) 相应位置加元素
        • notes.remove(index) 返回remove的元素
        • notes.toArray(a) 将内容填充到数组a中
    • 可使用for-each循环
  • 对象数组

    对象不存在则为null

    • String[] 里面的元素是String类的管理者(和基础类型数组的区别)
    • 可定义String a = null
    • length()可获得数组的长度
    • for-each循环,以下代码是可实现的(和基础类型数组的区别)
    //对字符串数组a而言
    String[] a = new String[10];
    for (String k : a){
    	k = "good";
    }
    
  • 集合容器

    • HashSet< String >
    • 无顺序,元素不能重复
    • toString()方法使得该容器可以直接输出
    class Value{
    	public String toString(){return ""+i;}
    }
    
  • Hash表

    • HashMap
    private HashMap<Interger,String> coinnames = new HashMap<Interger,String>();
    coinnames.put(key,value)
    coinnames.keySet() //得到key表
    coinnames.keySet().size()
    coinnames.get(index)
    

继承(Inherited)

基于已有的设计创造新的设计,就是面向对象程序设计的继承
代码复制是代码质量不良的表现,不好维护

  • 基础派生出其他类的那个类叫做父类(超类,基类),派生出来的叫子类。
    Java用关键字extends来表示这种继承/派生关系
    class ThisClass extends SuperClass{//…}
  • 子类的对象对可以看做是父类的对象。
  • Java只允许单继承,即一个类只能有一个父类
  • 父类所有的成员都会被子类继承,都是存在的,都是可能可以用的(还要考虑访问权限)

public:所有人都能访问
protected:包内和包外的子类可以访问
缺省:包内可以访问
private:只有类自己可以访问

java进阶(面向对象编程)_第1张图片

  • public的成员直接成为子类的public的成员,protected的成员也直接成为子类的protected的成员。Java的protected的意思是包内和子类可访问,所以它比缺省的访问属性要宽一些。而对于父类的缺省的未定义访问属性的成员来说,他们是在父类所在的包内可见,如果子类不属于父类的包,那么在子类里面,这些缺省属性的成员和private的成员是一样的:不可见。父类的private的成员在子类里仍然是存在的,只是子类中不能直接访问。我们不可以在子类中重新定义继承得到的成员的访问属性。如果我们试图重新定义一个在父类中已经存在的成员变量,那么我们是在定义一个与父类的成员变量完全无关的变量,在子类中我们可以访问这个定义在子类中的变量,在父类的方法中访问父类的那个。尽管它们同名但是互不影响。
  • 子类通过super()来调用父类的构造函数,从而构造自己的成员变量。

构造子类对象时,先进构造函数头,再进父类的构造函数初始化,再进入定义初始化,再去做构造函数的剩下部分。
可以理解为:
父类的定义初始化和构造初始化做完后,再做子类的定义和构造初始化
没有super()会默认去找父类中没有参数的构造器,如果有super(),就根据super()中的参数来寻找父类的构造函数。

  • 在构造一个子类的对象时,父类的构造方法也是会被调用的,而且父类的构造方法在子类的构造方法之前被调用。在程序运行过程中,子类对象的一部分空间存放的是父类对象。因为子类从父类得到继承,在子类对象初始化过程中可能会使用到父类的成员。所以父类的空间正是要先被初始化的,然后子类的空间才得到初始化。在这个过程中,如果父类的构造方法需要参数,如何传递参数就很重要了。

  • 子类如果有函数和父类同名,使用super.func()来调用父类的那个函数(父类这个函数是可访问时)

  • 父类item和子类DVD

//Item.java
package database;

public class Item {
	private String title;
	private int playingTime;
	private boolean gotIt;
	private String comment;
		
	public Item(String title, int playingTime, boolean gotIt, String comment) {
		this.title = title;
		this.playingTime = playingTime;
		this.gotIt = gotIt;
		this.comment = comment;
	}

//	public void setTitle(String title) {
//		this.title = title;
//	}
	
	public void print() {
		System.out.print(title+" ");
	
	}
	public static void main(String[] args){
	}
}
//DVD.java
package database;

public class DVD extends Item {
	private String director;
	
	public DVD(String title,String director,int playingTime, boolean gotIt, String comment) {
		super(title,playingTime,gotIt,comment);
		this.director = director;
	}
	
	public void print() {
		System.out.print("DVD: ");
		super.print();
		System.out.print(director);
	}
	
	public static void main(String[] args){
		DVD dvd = new DVD("lovelyy","wjj",120,false,"...");
		dvd.print();
	}
}

多态

  • 子类和子类型
    • 类定义了类型
    • 子类定义了子类型
    • 子类的对象可以被当做父类的对象来使用
      1. 赋值给父类的变量
      2. 传递给需要父类对象的函数
      3. 放进存放父类对象的容器里
  • 多态变量(声明(静态)类型,动态类型)
    • java的对象变量是多态的,它们能保存不止一种类型的对象
    • 它们可以保存的是声明类型的对象,或者声明类型的子类的对象
    • 当把子类的对象赋给父类的变量的时候,就发生了向上造型
  • 造型cast
    • 子类的对象可以赋值给父类的变量(向上造型)
      注意!java中不存在对象对对象的赋值,是对象的管理者同时管理(或者说管理权限的变化)
    • 父类的对象不能直接赋值给子类的变量(除非Person p = (Person)obj这样)
  • 用括号围起来的类型放在值的前面称为造型
  • 对象本身没有发生任何变化,所以不是“类型转换(int i = (int)10.2)”,要区分(虽然英语的单词都是cast)!
  • 运行时有机制来检查这样的转化是否合理(ClassCastException)
  • 向上造型
    • 拿一个子类的对象,当做父类对象来用
      对于DVD对象dvd,Item类型变量i
      i = dvd
    • 向上造型是默认的,不需要运算符
    • 向上造型是安全的
  • 向下造型例子
    对于DVD变量d,Item类对象v
    d = (DVD) item;
    需要v被指向一个DVD这个子类对象。如果没有指向,那在编译时时没问题的,但是运行时会产生错误
  • 多态

    • 函数调用的绑定(对多态变量而言(如item))
      • 当通过对变量对象调用函数时,调用哪个函数这件事情叫做绑定
        1. 静态绑定:根据变量的声明类型来决定
        2. 动态绑定:根据变量的动态类型来决定(java都是动态绑定)
      • 在成员函数中调用其他成员函数也是通过this这个对象变量来调用的(看做动态绑定)
    • 覆盖(override)
      • 子类和父类存在名称和参数表完全相同的函数,这一对函数构成覆盖关系
      • 通过父类的变量调用存在覆盖关系的函数时,会调用变量当时所管理的对象所属的类的函数
  • Object类
    java是单根结构,所有的类都是Object类的子类,Object类是root类

    • Object类的函数
      Obeject o = new Object();
      o. 来查看有哪些函数
      • toString() (如果不覆盖(override),默认Object类的toString()会输出package.Class@e76cbf7)
        如果对一个对象进行字符串加法,会寻找是否有这个方法,如果有就能进行字符串的加法
      • equals()(默认来查看是否管理的是同一个对象)
        可覆盖(override)来设置该函数为:查看某个成员变量是否相等
      @override
      public boolean equals(Object obj){
      	CD cc = (CD)obj;
      	return artist.equals(cc.artist);
      }
      
    • @Override表示(为了告诉编译器)以下部分的函数,是覆盖了父类的函数。和父类的函数具有完全相同的函数签名(函数原型):函数的名字和函数的参数表都相同,而且都是public(权限也不能变)
  • 例子:dome的新媒体类型

    • 代码的可拓展性,与可维护性不同(可维护性是易于修改,可拓展性是易于增加)
    • 子类的底下还可以再派生子类,生成更深的继承关系

设计原则

  • 代码复制问题
    编写函数方法来解决
  • 封装
    通过封装来降低耦合(通过接口来解耦)
    • 类和类之间的关系称为耦合
      高耦合的表现:两类都有大量的代码和出口相关,大量使用另外一个类的成员变量
    • 耦合越低越好

String是一个immutable(不可修改)的对象,当需要很多复杂的字符串操作来产生一个结果字符串时,为防止系统开销过大,可使用StringBuffer对象,这是一个可以不断修改的对象。最后用toString()产生一个String对象就可以

  • 可拓展性
    • 通过接口来实现聚合
    • 通过容器来实现灵活性
      • Hash表
  • 框架加数据
    • 以框架+数据来提高可拓展性
      • 命令的解析是否可以脱离if-else
      • 定义一个Handler来处理命令
      • 用Hash表来保存命令和Handler之间的关系
    • 城堡游戏
//Game(Main).java
package castle;

import java.util.HashMap;
import java.util.Scanner;

public class Game {
    private Room currentRoom;
    private HashMap<String, Handler> handlers = new HashMap<String, Handler>();
    public Game() 
    {
    	handlers.put("go", new HandlerGo(this));
    	handlers.put("help",new HandlerHelp(this));
    	handlers.put("bye", new HandlerBye(this));
        createRooms();
    }

    private void createRooms()
    {
        Room outside, lobby, pub, study, bedroom;
      
        //	制造房间
        outside = new Room("城堡外");
        lobby = new Room("大堂");
        pub = new Room("小酒吧");
        study = new Room("书房");
        bedroom = new Room("卧室");
        
        //	初始化房间的出口
        outside.setExit("east", lobby);
        outside.setExit("south",study);
        outside.setExit("west",pub);
        lobby.setExit("west", outside);
        pub.setExit("east", outside);
        study.setExit("north",outside);
        study.setExit("east",bedroom);
        bedroom.setExit("west", study);
        lobby.setExit("up", pub);
        pub.setExit("down", lobby);
        currentRoom = outside;  //	从城堡门外开始
    }

    private void printWelcome() {
        System.out.println();
        System.out.println("欢迎来到城堡!");
        System.out.println("这是一个超级无聊的游戏。");
        System.out.println("如果需要帮助,请输入 'help' 。");
        System.out.println();
        showPrompt();

    }

    // 以下为用户命令

    public void goRoom(String direction) 
    {
        Room nextRoom = currentRoom.getExit(direction);
  
        if (nextRoom == null) {
            System.out.println("那里没有门!");
        }
        else {
            currentRoom = nextRoom;
            showPrompt();
        }
    }
	
    public void showPrompt() {
        System.out.println("现在你在" + currentRoom);
        System.out.print("出口有:");
        System.out.print(currentRoom.getExitDesc());
        System.out.println();
    }
    
    public void play() {
    	Scanner in = new Scanner(System.in);
    	while ( true ) {
     		String line = in.nextLine();
     		String[] words = line.split(" ");
     		Handler handler = handlers.get(words[0]);
     		String value ="";
     		if (words.length > 1)
     			value = words[1];
     		if (handler != null) {
     			handler.doCmd(value);
     			if (handler.isBye()) 
     				break;
     		}
//     		if ( words[0].equals("help") ) {
//     			game.printHelp();
//     		} else if (words[0].equals("go") ) {
//     			game.goRoom(words[1]);
//     		} else if ( words[0].equals("bye") ) {
//     			break;
//     		}
    	}
     	in.close();
    }
	public static void main(String[] args) {
		Game game = new Game();
		game.printWelcome();
		game.play();
 		System.out.println("感谢您的光临。再见!");
	}

}

//Room.java
package castle;

import java.util.HashMap;

public class Room {
    private String description;
    private HashMap<String,Room> exits = new HashMap <String, Room>();
    
    public Room(String description) 
    {
        this.description = description;
    }
    
    public String getExitDesc() {
    	StringBuffer sb = new StringBuffer();
    	for (String dir : exits.keySet()) {
    		sb.append(dir);
    		sb.append(" ");
    	}
    	return sb.toString();
    }

    public Room getExit(String direction) {
		return exits.get(direction);
    }

    public void setExit(String dir, Room room) {
    	exits.put(dir,room);
    }

    @Override
    public String toString()
    {
        return description;
    }
}

//Handler.java
package castle;

public class Handler {
	protected Game game;
	
	public Handler(Game game) {
		this.game = game;
	}
	
	public void doCmd(String word){}
	
	public boolean isBye() {
		return false;
	}
}

//HandlerBye
package castle;

public class HandlerBye extends Handler {
	public HandlerBye(Game game) {
		super(game);
	}
	
	@Override
	public boolean isBye() {
		return true;
	}

}

//HandlerHelp
package castle;

public class HandlerHelp extends Handler {
	public HandlerHelp(Game game) {
		super(game);
	}
	
	@Override
	public void doCmd(String word) {
		System.out.println("迷路了吗?你可以做的命令有:go bye help");
        System.out.println("如:\tgo east");
	}

}

//HandlerGo
package castle;

public class HandlerGo extends Handler {
	
	public HandlerGo(Game game) {
		super(game);
	}
	
	@Override
	public void doCmd(String word) {
		game.goRoom(word);
	}
	
}

抽象与接口

  • 抽象
    • 抽象函数——表达概念而无法实现具体代码的函数
      • 带有abstract修饰符的函数
      • 有抽象函数的一定是抽象类
    • 抽象类——表达概念而无法构造出实体的类
      • 抽象类不能制造对象,但是可以定义变量
        • 任何继承了抽象类的非抽象类的对象可以赋给这个变量(称为实现)
        • 实现抽象函数
          • 继承自抽象类的子类必须覆盖父类中的抽象函数,否则自己成为抽象类
    • 两种抽象
      • 与具体相对——表示一种概念而非实体
      • 与细节相对——表示在一定程度上忽略细节而着眼大局
public abstract class Shape {
	public abstract void draw(Graphics g);	
}
  • 数据(Data)和表现(representation)分离
    • 程序的业务逻辑和表现无关
      • 表现可以使图形的也可以是文本的
      • 表现可以是当地的也可以是远程的
    • 表现与数据的关系
    • 责任驱动的设计
      • 将程序要实现的功能分配到合适的类/对象中去是设计中非常重要的一环
    • 网格化
      • 图形界面本身有更高的解析度
      • 但是将画面网格化以后,数据就更容易处理了
  • 接口(interface)
    • 接口都是纯抽象类
      • 所有的成员函数都是抽象函数(都不用声明public abstract)
      • 所有的成员变量都是public static final
    • 接口规定了长什么样,但不管里面有什么
    • 继承用extends,接口用implements
    • 实现接口
      • 类可以实现很多接口
      • 接口可以继承接口,但不能继承类
      • 接口不能实现接口
    • 面向接口的编程方式
      • 其实接口是由类决定的
      • 设计程序时先定义接口,再实现类
      • 任何需要在函数间传入传出的一定是接口而不是具体的类
      • 是java成功的关键之一,因为极适合多人同时写一个大程序
      • 也是Java被批评的要点之一,因为代码量膨胀起来很快
    • Cell和Field的关系
      • Cell在Field中,但是很多操作需要Field的数据
        • 方法一:让每个Cell有一个Field的管理者(Cell知道Field)
        • 方法二:由外部第三方来建立两者之间的联系(Cell不知道Field)

接口所有的方法都是没有方法体的,而且都是public abstract,即使你没有这样声明。而接口中的所有成员变量都是public static final的变量,并且必须有定义初始化,因为所有变量都必须在编译的时候有确定值。
接口和类的地位是一样的。因此我们可以看到其定义方式跟类也是基本上一样的。当然,其中的所有方法都没有方法体。而当类从接口得到继承的时候,Java用另外一个动词:实现(implements)。当然,实现接口的类必须覆盖接口中所有的方法,否则这个类就成为一个抽象类。
Java不允许多继承,但是允许一个类实现多个接口,也允许一个接口从多个接口得到继承,但是不允许接口从类继承。

//Cell.java
package cell;

import java.awt.Graphics;

public interface Cell{
	void draw(Graphics g,int x,int y,int size);
}
//Fox.java
package animal;

import java.awt.Color;

public class Fox extends Animal implements Cell {
	public Fox() {
		super(20,4);
	}
	
	@Override
	public void draw(Graphics g, int x, int y, int size) {
		int alpha = (int)((1-getAgePercent())*255);
		g.setColor(new Color(0, 0, 0, alpha));//(int)((20-getAge())/20.0*255)));
		g.fillRect(x, y, size, size);
	}

	@Override
	public Animal breed() {
		Animal ret = null;
		if ( isBreedable() && Math.random() < 0.05 ) {
			ret = new Fox();
		}
		return ret;
	}
	
	@Override
	public String toString() {
		return "Fox:"+super.toString();
	}

	@Override
	public Animal feed(ArrayList<Animal> neighbour) {
		Animal ret = null;
		if ( Math.random()< 0.2 ) {
			ret = neighbour.get((int)(Math.random()*neighbour.size()));
			longerLife(2);
		}
		return ret;
	}
}

反转控制与MVC模式

  • (Swing框架)Jframe的布局管理器(Layout Manager)是BorderLayout
    • North,South,West,East,Center
  • 用户在图形界面上的操作让程序知道,称为消息机制
  • 反转控制(注入反转)————Swing的消息机制
  • 内部类
    • 内部类
      • 定义在别的类的内部、函数内部的类
      • 内部类能直接访问外部的全部资源
        • 包括私有的成员
        • 外部是函数时,函数所在的类的成员也一样可以去访问,但是访问函数的本地变量时,只能访问函数里final的变量
    • 匿名类
      • 在new对象的时候给出类的定义形成了匿名类
      • 匿名类可以继承某类,也可以实现某接口
      • Swing的消息机制广泛使用匿名类
    • 注入反转
      • 由按钮公布一个守听者接口和一对注册/注销函数
      • 你的代码实现那个接口,将守听者对象注册在按钮上
      • 一旦按钮被按下,就会反过来调用你的守听者对象的某个函数

内部类就是指一个类定义在另一个类的内部,从而成为外部类的一个成员。因此一个类中可以有成员变量、方法,还可以有内部类。实际上Java的内部类可以被称为成员类,内部类实际上是它所在类的成员。所以内部类也就具有和成员变量、成员方法相同的性质。比如,成员方法可以访问私有变量,那么成员类也可以访问私有变量了。也就是说,成员类中的成员方法都可以访问成员类所在类的私有变量。内部类最重要的特点就是能够访问外部类的所有成员

  • MVC设计模式

JTable可以以表格的形式显示和编辑数据。JTable类的对象并不存储数据,它只是数据的表现。数据和表现是分离的

  • 数据、表现和控制三者分离,各负其责
    • M = Model 模型
    • V = View 表现
    • C = Control 控制
  • 模型:保存和维护数据,提供接口让外部修改数据,通知表现需要刷新
  • 表现:从模型获得数据,根据数据画出表现
  • 控制:从用户得到输入,根据输入调整数据
    == 控制和表现没有直接联系!!具体实现中View和Contol常常合并起来,但这并不矛盾==

异常处理(exception)

  • 异常
try{
//可能产生异常的代码
} catch(Exception e1){
//处理e1异常的代码
} catch(Exception e2){
//处理e2异常的代码
}

出现异常后一直往外层找try,简单说就是异常会由内向外抛出,知道被捕捉到,或者在最外层也没有捕捉到,就会输出异常,终止程序。

catch(ArrayIndexOutOfBoundsException e){
	System.out.println(e.getMessage());
	System.out.printlin(e);
	e.printStackTrace();//打印出调用堆栈追踪
}
  • 捕捉异常之后(拿到异常对象后)
    • getMessage();
    • toString();
    • void printStackTrace();
    • 肯定是回不去了,而具体的处理逻辑则取决于你的业务逻辑需要
    • 如果在这个层面上需要处理,但是不能做最终的决定则可以再度抛出
//catch(Exception e)会捕捉所有的异常
catch(Exception e){
	System.err.println("An exception was throw");
	throw e;//捕捉到处理了之后想再度抛出
  • 异常机制
    • 如果要读文件
      • 打开文件,判断文件大小,分配足够的内存空间,把文件读进内存,关闭文件
try{
	open the file;
	determine its size;
	allocate that much memory;
	read the file into memory;
	close the file;
}catch(fileOPenFailed){
	doSomethin;
}catch(sizeDeterminationFailed){
	doSomething;
}......

异常机制最大的好处就是清晰的分开了正常的业务逻辑代码和遇到情况时的处理代码
异常发生时必须停下了让其他地方的代码来处理执行

  • 异常抛出与声明
    • 如果你的函数可能抛出异常,就必须在函数的头部加以声明
    • 你可以声明并不会真的抛出的异常,没有影响
    • 什么能扔
      • 任何继承了Throwable类的对象
      • Exception类继承了Throwable
      • throw new Exception();
      • throw new Exception(“HELP”)
public class TooBig extends Throwable{}
public class TooSmall extends Throwable{}
public class DivZero extends TooSmall{}
public class Test{ 
	void f() throws TooBig,TooSmall,DivZero{
		throws new DivZero();
	}
	public static void main(String[] args){
		try{
			f();
		}catch(TooSmall e){
			//doSomething
		}
	}
}

  • 异常捕捉时的匹配
    • catch怎么匹配异常的
      • is-A的关系
      • 就是说抛出子类异常会被捕捉父类异常的catch给捕捉到的
    • 运行时刻异常
      • 像ArrayIndexOutOfBoundsException这样的异常不用声明,是自带的,但是如果没有适当的机制来捕捉,就会最终导致程序终止
    • 如果你调用一个声明会抛出异常的函数,那你必须:
      • 把函数的调用放在try块中,并且设置catch来捕捉所有可能抛出的异常
      • 声明自己会抛出无法处理的异常
    • 异常声明遇到继承关系
      • 当覆盖一个成员函数,子类不能声明抛出比父类版本更多的异常
      • 在子类的构造函数中,必须声明父类可能抛出的全部异常。还可以增加(与成员函数的比较下,成员函数和构造函数的关系是不同的!!好好理解,可以再去看)

输入与输入

  • 流(处理输入输出的方式)
    • 流的基础类(抽象类)
      • InputStream
        read(),int read(),mark(),reset(),close(),int available()
      • OutputStream
        write(),flush(),close()
  • 文件流
    • FileInputStream
    • FileOutputStream
    • 对文件作读写操作
    • 实际工程中已经较少使用
      • 更常用的是以在内存数据或者通信数据上建立的流,如数据库的二进制数据对下或者网络端口通信
      • 具体的文件读写往往有更专业的类,比如配置文件和日志文件
  • 流过滤器
    • 以一个介质流对象为基础层层构建过滤器流,最终形成的刘对象在数据的输入输出过程中,逐层使用过滤器流的方法来读写数据
    • 数据的读入。
      二进制文件的字节流→FileInputStream(只能读字节)→BufferedInputStream(缓冲)→DataInputStream(读基本数据类型)→内存的基本数据类型
    • Data
      • DataInputStream
      • DataOutputStream
      • 用以读写二进制表达的基本数据类型的数据
byte[] buf = new byte[10];
for (int i=0;i<buf.length;i++){
	buf[i]=(byte)i;
}
try{
	int i = Oxcafebabe;//int默认10进制,0b表示2进制,0x表示16进制
	//输出流(内存的二进制数据的写入dat文件)
	DataOutputStream out = new DataOutputStream(
		new BufferedOutputStream(
			new FileOutputStream("a.dat")));
	out.writeInt(i)
	out.close;
	//输入流(dat文件二进制数据读入内存)
	DataInputStream in = new DataInputStream(
		new BufferedInputStream(
			new FileInputStream("a.dat")));
	int j = in.readInt();
	System.out.println(j)
  • 文本流
    • Reader/Writer
      • 二进制数据采用InputStream/OutputStream
      • 文本数据采用Reader/Writer(处理Unicode字符的)
    • 如果文件不是Uniocde编码的,需要借助流来打开然后过滤来处理文本文件
    • Reader
      • 常用BufferedReader
      • readLine()
      • FileReader
        • InputStreamReader的子类,所有的方法都从父类继承而来
        • 直接可以FileReader(File file)
    • LineNumberReader
      • 可以得到行号,getLineNumber()
try{
   //文本数据写入文本文件
   PrintWriter out = new PrintWriter( 
   	new BufferedWriter(  
   		new OutputStreamWriter(  //构建stream和writer的桥梁
   			new FileOutputStream("a.txt")));
   int i = 123456;
   out.printInt(i);
   out.close();
   //文本文件写入内存
   	BufferedReader in = new BufferedReader( 
   		new InputStreamReader(  //构建stream和Reader的桥梁
   			new FileOutputStream("src/hello/Main.java")));
   String line;
   while ((line=in.readLine())!=null){
   	System.out.println(line);
   }
  • 汉字编码
 	//文本文件写入内存
		BufferedReader in = new BufferedReader( 
			new InputStreamReader(  //由这个来管理
				new FileOutputStream("utf-8.txt"),"utf-8"));
	String line;
	while ((line=in.readLine())!=null){
		System.out.println(line);
  • 格式化输入输出

  • printWriter(输出)

    • format(“格式”,…)
    • printf(“格式”,…)
    • print(各种基本类型)
    • println(各种基本类型)
  • Scanner(输入)

    • 在Inputstream或者Reader上建立一个Scanner对象可以从流中的文本中解析出以文本表达的各种基本类型
    • next…()
  • 流的应用

    • read()函数是阻塞的,在读到所学的内容之前会停下来等
      • 使用read()的更高级的函数,如nextInt(),readLine()都是这样的
      • 所以常用单独的线程来做socket读的等待,或使用nio的channel选择机制
    • 对于socket,可设置SO时间
      • setSoTimeout(int timeOut)
package stream;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

public class Main {

	public static void main(String[] args) {
		try {
			Socket socket = new Socket(InetAddress.getByName("localhost"),12345);
			PrintWriter out = new PrintWriter( 
					new BufferedWriter(  
						new OutputStreamWriter(
								socket.getOutputStream())));
			out.println("Hello");
			out.flush();
			BufferedReader in = new BufferedReader( 
					new InputStreamReader(
							socket.getInputStream()));
			String line;
			line = in.readLine();
			System.out.println(line);
			out.close();
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
  • 对象串行化
    • ObjectInputStream类
      • readObject()
    • ObjectOutputStream类
      • writeObject()
    • Serializable接口
package stream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Student implements Serializable{
	private String name;
	private int age;
	private int grade;
	public Student(String name, int age, int grade) {
		this.name = name;
		this.age = age;
		this.grade = grade;
	}
	
	public String toString() {
		return name +" " + age +" " + grade;
	}
	
}
	
public class ObjectStream {

	public static void main(String[] args) {
		Student s1 = new Student("wjj",23,100);
		System.out.println(s1);
		try {
			ObjectOutputStream out = new ObjectOutputStream(
					new FileOutputStream("obj.dat"));
			out.writeObject(s1);
			out.close();
			ObjectInputStream in = new ObjectInputStream(
					new FileInputStream("obj.dat"));
			Student s2 = (Student)in.readObject();
			System.out.println(s2);
			in.close();
			System.out.println(s1==s2);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

学完了,感谢翁恺老师的Java课程

黑马程序员补充

静态代码块

  • 格式
    在java类中(方法中不能存在静态代码块)使用static关键字和{}声明的代码块.
  • 执行时机
      静态代码块在类被加载的时候就运行了,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。后面在比较的时候会通过具体实例来证明。
  • 静态代码块的作用
      一般情况下,如果有些代码需要在项目启动的时候就执行,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件等资源,我们就可以都放入静态代码块中
  • 静态代码块不能存在任何方法体中
  • 静态代码块不能访问普通变量

构造代码块

  • 格式
      在java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字):
  • 执行时机
      构造代码块在创建对象时被调用,每次创建对象都会调用一次,但是优先于构造函数执行。
  • 构造代码块的作用
      和构造函数的作用类似,都能对对象进行初始化,并且只要创建一个对象,构造代码块都会执行一次。但是反过来,构造函数则不一定每个对象建立时都执行(多个构造函数情况下,建立对象时传入的参数不同则初始化使用对应的构造函数)。
      利用每次创建对象的时候都会提前调用一次构造代码块特性,我们可以做诸如统计创建对象的次数等功能。

参考链接 https://www.cnblogs.com/ysocean/p/8194428.html
执行:静态代码块→构造方法块→构造函数→普通函数

继承

  • 继承关系中,父子类构造方法的访问特点
    1. 子类构造方法中有一个默认的"super()"调用,所以一定是先调用父类的构造,后执行子类的构造
    2. 可以通过super关键字来子类狗仔调用父类重载构造
    3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。

总结: 子类必须调用父类构造方法,不写则赠送super(),,写了则调用指定的super调用,super只能有一个且必须为第一个语句。

  • super和this关键字的三种用法
    • super
      1. 在子类的成员方法中,访问父类的成员变量
      2. 在子类的成员方法中,访问父类的成员方法
      3. 在子类的构造方法中,访问父类的构造方法
    • this
      1. 在本类的成员方法中,访问本类的成员变量
      2. 在本类的成员方法中,访问本类的另一个成员方法
      3. 在本类的构造方法中,访问本类的另一个构造方法(this也必须也要是构造方法的第一个语句,唯一一个。super和this两种构造调用不能同时使用
  • Java继承的三个特点
    1. Java语言是单继承的
    2. Java可以多级继承
    3. 一个父类可以拥有很多子类

抽象类

  • 抽象方法
    加上abstract关键字(代表eat(),但是吃什么不确定)
    public abstract void eat()//不能有大括号方法体
  • 抽象类
    抽象方法所在的类必须是抽象类,加上abstract(如class Animal())
  • 抽象方法和类的使用
    • 不能直接创建抽象类对象,否则会编译错误,只能创建非抽象子类的对象
    • 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类对象使用的
    • 抽象类中,不一定包含抽象方法,但有抽象方法的一定是抽象类
    • 抽象类的子类必须重写父类中所有的抽象方法,否则编译无法通过而报错,除非该子类也是抽象类

接口(不含普通成员方法)

接口的定义类可以添加自己多的成员方法
是多个类的规范标准,只要符合,就可以大家通用

  • 接口的定义
public interface 接口名称{
	//接口内容
	//可以包含1常量,2抽象方法,(java8之后)3默认方法,4静态方法(java9后增加了5私有方法)
}
// 换成关键字interface之后,编译生成的字节码文件依然是.java→.class
  • 接口的抽象方法定义与使用
//接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
//这两个关键字修饰符可以选择性的忽略
public interface InterfaceAbs{
	public abstract voidmethodAbs();
}

如果实现类没有覆盖重写接口中所有的抽象方法,那么这个类自己就必须是抽象类
1.接口不能直接使用,必须有一个实现类来实现该接口
格式:public class 实现类名称 implements接口名称{}
2. 接口的实现类必须覆盖重写(实现)接口中的所有抽象方法。
实现:去掉abstract关键字,加上方法体大括号
3. 创建实现类的对象,进行使用

  • 默认方法的定义与使用
    格式: public default 返回值类型 方法名称(参数列表){有方法体}
    接口当中的默认方法,可以解决接口升级的问题

public可以省略,default不能省略

  1. 接口的默认方法,可以通过接口实现类对象,直接调用
  2. 接口的默认方法,也可以被接口的实现类覆盖重写
    这样看,和普通的成员方法很类似
  • 接口的静态方法定义和使用
    public static 返回值 方法名(参数列表){有方法体}
    • 和普通类中的静态方法的区别是:不能通过接口实现类的对象来调用接口中的静态方法,应使用接口名称直接调用其中的静态方法
      很多API就是这么做的

public可以省略

  • 接口的私有方法定义和使用
    我们需要抽取一个共有方法,不应该让实现类使用,应该是私有化的。

    1. 普通私有方法:解决多个默认方法之间重复代码问题
      private 返回值类型 方法名(参数列表){方法体}
    2. 静态私有方法:解决多个静态方法之间重复代码问题
      private static 返回值类型 方法名(参数列表){方法体}
  • 常量(接口中的成员变量)
    接口中也可以定义“成员变量”,但是必须使用public static final修饰,从效果上看,这其实就是接口的常量
    final关键字一旦赋值,不可改变

    • 可以省略public static final
    • 接口当中的常量,必须赋值,不能不赋值(不像普通类中成员变量不赋值就有默认值(0,false,null.0.0这些))。
    • 接口常量的名称,使用完全大写的字母,并用下划线分隔NUM_OF_CLASS
  • 注意事项与总结

    • 接口不能有静态代码块和构造方法
    • 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
      public class Myinterface implements Interface1,Interface2{}
    • 如果实现类所实现的多个接口当中,存在重复的抽象方法,只需要覆盖重写一次就可以,就把两个都覆盖了
    • 如果实现类没有覆盖重写所有接口当中的抽象方法,那实现类就必须是抽象类
    • 如果实现类所实现的多个接口中,存在重复的默认方法,实现类一定要对冲突的默认方法覆盖重写
    • 一个类如果继承父类当中的方法,和接口当中的默认方法产生冲突,优先用父类当中的方法(Java中继承是优先于接口实现的)
  • 类与类之间的关系

    • 类与类之间是单继承的,直接父类只有一个
    • 类与接口之间是多实现的,一个类可以实现多个接口
    • 接口与接口之间是多继承的
      public interface MyInterface extends MyInterfaceA,MyInterfaceB{}
    • 注意事项
      • 多个父接口当中的抽象方法如果重复,没关系
      • 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且要带着default关键字

多态

extends和implements都适用多态
父类引用指向子类对象称为多态

  • 多态访问成员变量的两种方式

    1. 直接通过变量名称访问(等号左边是谁就是谁),没有则向上查找
    Fu obj() = new Zi();
    System.out,println(obj.num);//用的Fu的成员变量
    

    理解成员变量没有覆盖重写这一说!!!

    1. 简介通过成员方法访问成员变量,看属于谁,就优先用谁,没有则向上找。
  • 多态访问成员方法的两种方式

    1. new的是谁,就先用谁,没有则向上找

    总结:
    成员变量:编译成功否看左,运行还看左(等号左右)
    成员方法:编译成功否看左,运行看右(等号左右)

  • 多态的作用
    无论右边new的时候换成哪个子类对象,等号左边调用方法都不会变化

  • 向上转型
    作用见上
    其实就是多态写法,父类引用指向子类对象
    父类名称 对象名 = new 子类对象();
    Animal animal = new Cat();

向上转型一定是安全的,因为范围由小变大。
弊端:一旦向上转型为父类,那么就无法调用子类特有的方法
解决方案,使用对象的向下转型

  • 向下转型
    如果需要用子类特有方法时要用
    其实是一个还原动作,将父类对象还原成本来的子类对象
    子类名称 对象名 = (子类名称) 父类对象;
    Animal animal = new Cat();
    Cat cat = (Cat)animal;

必须保证对象本来创建的时候就是猫,向下转型才能成为猫
如果对象创建的时候本来不是猫,现在非要向下转型成猫,就会报错

  • 用instanceof关键字判断对象类型
    对象 instanceof 类名称
    返回布尔值,可用if做判断
    使用向下转型一定要用instanceof来进行类型判断再做相应操作

  • 案例
    有了USB接口这个统一标准,笔记本电脑只要把参数写成usb就行了。外接设备只要能实现USB功能就可以和笔记本电脑联系起来(没有直接联系,通过usb接口间接联系)。

final关键字

不可变,最终的

  • 四种用法
    修饰类,修饰方法,修饰局部变量,修饰成员变量
  • 修饰类
    不能有任何的子类(太监类),但可以有父类。
    所有的成员方法无法进行覆盖重写
  • 修饰成员方法
    该方法不可被覆盖重写,是最终方法

对类、方法来说,abstract和final关键字不能同时使用,因为矛盾

  • 修饰局部变量(方法的大括号内,还有参数表内)
    一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。
    对基本类型,不可变是指变量当中的数据不可变
    对引用类型,不可变是指变量当中的地址值不可变,但内容可以变
  • 修饰成员变量
    对于成员变量,如果使用final关键字修饰,那么变量也照样不可变。
    1. 有与成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
    2. 对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。(有且必须用一个)
    3. 必须保证类当中所有重载的构造方法,都能最终会对final进行赋值

权限修饰符

  • public protected default private
    • 同一个类
    • 同一个包
    • 不同包子类
    • 不同包非子类

内部类

一个类包含另一个类

  • 分类
  • 成员内部类
    • 内部类可以访问外部类的变量,外用内需要内部类对象。
    • 使用方法:
      1. 间接方式:在外部类的方法中,使用内部类,然后main只是调用外部类的方法。
      2. 直接方式:外部类名.内部类名 = new 外部类名称().new 内部类名()
    • 内部类的同名变量访问
      局部变量:name(就近原则)
      内部类的成员变量:this.name
      外部类的成员变量:Outer.this.name
//成员内部类直接使用方式
Body.Heart heart = new Body().new Heart();

- 
  • 局部内部类(其中含有匿名内部类)
    定义在一个方法内部,只要当前所属的方法才能使用,外面不能调用
    注意事项:局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】
  • 匿名内部类
    • 如果接口的实现类(或者父类的子类)只需要使用唯一一次,那么可以省略该类的定义,改为使用【匿名内部类】
    • 注意事项
      1. 匿名内部类,在【创建对象】时,只能使用唯一一次
        想用多次就得用单独定义的实现类
      2. 匿名对象,在【调用方法】时,只能使用唯一一次
        想用多次就必须建立引用(取名字)
      3. 区别匿名对象和匿名内部类!!
//使用匿名内部类
Myinterface obj = new MyInterface(){
	@override
	public void method(){
		System.out.println("a");
	}
};
  • 类和接口都可以作为成员变量类型

  • 接口也可以作为方法的参数和返回值

    • java.util.List是ArrayList所实现的接口

只要涉及到类型,任何数据类型都能使用。

  • 发红包案例

你可能感兴趣的:(java学习)