1. 面向对象和面向过程的区别
面向过程:就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象:就是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
2. 对象和类
2.1 对象
万物皆对象:
软件系统所模拟的真实世界中,所有的实体都可以抽象为对象
每个对象都是唯一的
对象具有属性和行为(方法)
对象具有状态:
状态指某个瞬间对象各种属性的取值
对象的方法可以改变对象自身的状态
对象都属于某个类,每个对象都是某个类的实例
2.2 类
类是一组具有相同属性和行为的对象的抽象
开发人员自定义数据类型
面向对象编程的主要任务就是定义各个类
对象是类的实例,类是对象的模板
类的认识重点介绍类和对象的基本定义,属性和方法的基本使用方式。
属性(field 成员变量)
属性用于定义该类或该类对象包含的数据或者说静态特征。属性作用范围是整个类体。
在定义成员变量时可以对其初始化,如果不对其初始化,Java使用默认的值对其初始化。
数据类型 | 默认值 |
---|---|
整型 | 0 |
浮点型 | 0.0 |
字符型 | ‘\u0000’ |
布尔型 | false |
所有引用类型 | null |
属性定义格式:
[修饰符] 属性类型 属性名 = [默认值] ;
方法
方法用于定义该类或该类实例的行为特征和功能实现。方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
方法定义格式:
[修饰符] 方法返回值类型 方法名(形参列表) {
// n条语句
}
【示例】类的定义方式
// 每一个源文件必须有且只有一个public class,并且类名和文件名保持一致!
public class Car {
}
class Tyre { // 一个Java文件可以同时定义多个class
}
class Engine {
}
class Seat {
}
上面的类定义好后,没有任何的其他信息,就跟我们拿到一张张图纸,但是纸上没有任何信息,这是一个空类,没有任何实际意义。所以,我们需要定义类的具体信息。对于一个类来说,一般有三种常见的成员:属性field、方法method、构造器constructor。这三种成员都可以定义零个或多个。
【示例】编写简单的学生类
public class SxtStu {
//属性(成员变量)
int id;
String sname;
int age;
//方法
void study(){
System.out.println("我正在学习!");
}
//构造方法
SxtStu(){
}
}
2.3 this关键字
2.3.1 对象创建的过程和this的本质
构造方法是创建Java对象的重要途径,通过new关键字调用构造器时,构造器也确实返回该类的对象,但这个对象并不是完全由构造器负责创建。创建一个对象分为如下四步:
(1)分配对象空间,并将对象成员变量初始化为0或空
(2)执行属性值的显式初始化
(3)执行构造方法
(4)返回对象的地址给相关的变量
this的本质就是“创建好的对象的地址”! 由于在构造方法调用前,对象已经创建。因此,在构造方法中也可以使用this代表“当前对象”。
2.3.2 代码实现
this普通用法:
public class User {
int id; //id
String name; //账户名
String pwd; //密码
public User() {
}
public User(int id, String name) {
System.out.println("正在初始化已经创建好的对象:"+this);
this.id = id; //不写this,无法区分局部变量id和成员变量id
this.name = name;
}
public void login(){
System.out.println(this.name+",要登录!"); //不写this效果一样
}
public static void main(String[ ] args) {
User u3 = new User(101,"张三");
System.out.println("打印张三对象:"+u3);
u3.login();
}
}
this()调用重载构造方法
public class TestThis {
int a, b, c;
TestThis() {
System.out.println("正要初始化一个Hello对象");
}
TestThis(int a, int b) {
// TestThis(); //这样是无法调用构造方法的!
this(); // 调用无参的构造方法,并且必须位于第一行!
a = a;// 这里都是指的局部变量而不是成员变量
// 这样就区分了成员变量和局部变量. 这种情况占了this使用情况大多数!
this.a = a;
this.b = b;
}
TestThis(int a, int b, int c) {
this(a, b); // 调用带参的构造方法,并且必须位于第一行!
this.c = c;
}
void sing() {
}
void eat() {
this.sing(); // 调用本类中的sing();
System.out.println("你妈妈喊你回家吃饭!");
}
public static void main(String[ ] args) {
TestThis hi = new TestThis(2, 3);
hi.eat();
}
}
2.4 static 关键字
2.4.1 static特点
在类中,用static声明的成员变量为静态成员变量,也称为类变量。 类变量的生命周期和类相同,在整个应用程序执行期间都有效。它有如下特点:
(1)为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化。
(2)对于该类的所有对象来说,static成员变量只有一份。被该类的所有对象共享!!
(3)一般用“类名.类属性/方法”来调用。(也可以通过对象引用或类名(不需要实例 化)访问静态成员。)
(4)在static方法中不可直接访问非static的成员。
2.4.2 代码实现
package test;
public class User2 {
private int id;
private String name;
private String pwd;
private static String company="北京烤鸭";
public User2(int id, String name) {
this.id = id;
this.name = name;
}
public void login(){
System.out.println(name+"已登录");
}
public static void showCompany(){
// login();调用非静态成员,编译报错
//System.out.println(id);
System.out.println(company);
}
public static void main(String[] args) {
User2 user2 = new User2(123,"张三");
user2.login();
//User2.login();无法使用类名调用非静态成员
//User2.id;
User2.showCompany();
User2.company="羊蝎子";
User2.showCompany();
}
}
张三已登录
北京烤鸭
羊蝎子
2.5 包机制
package在java中的作用类似于“操作系统中文件夹的作用”。主要解决两个问题:
(1)提供多层的命名空间,解决命名冲突;(处于不同package中的类可以拥有相同的名字)
(2)对类按功能进行分类,使得项目的组织更加清楚
JDK中的主要包
导入类import
如果我们要使用其他包的类,需要使用import导入,从而可以在本类中直接通过类名来调用,否则就需要书写类的完整包名和类名。import后,便于编写代码,提高可维护性。
注意要点
(1)Java会默认导入java.lang包下所有的类,因此这些类我们可以直接使用。
(2)如果导入两个同名的类,只能用包名+类名来显示调用相关类:
java.util.Date date = new java.util.Date();
3. 面向对象三大特征
3.1 封装(encapsulation)
3.1.1 封装的作用
隐藏对象的属性和实现细节,仅仅对外公开接口。
编程中封装的具体优点:
(1)提高代码的安全性。
(2)提高代码的复用性。
(3)“高内聚”:封装细节,便于修改内部代码,提高可维护性。
(4)“低耦合”:简化外部调用,便于调用者使用,便于扩展和协作。
3.1.2 封装的使用
Java是使用“访问控制符”来控制哪些细节需要封装,哪些细节需要暴露的。 java提供四种访问权限控制符,如下表:
注意:在继承中(继承在下面会详细介绍):
(1)若子类与父类在同一个包中,子类可以访问父类的protected成员,也可以访问父类对象的protected成员。
(2)若子类与父类不在同一个包中,子类可以访问父类的protected成员,但不可以访问父类对象的protected成员。(即,能用继承来的protected成员,但不能通过new 父类对象来调用protected成员)。
3.1.3 javaBean封装
package com.sxt.test;
public class TestEncapsulate {
private int id;
private String name;
private boolean man;
public TestEncapsulate() {
}
public TestEncapsulate(int id, String name, boolean man) {
this.id = id;
this.name = name;
this.man = man;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isMan() {
return man;
}
public void setMan(boolean man) {
this.man = man;
}
}
3.2 继承:(extends)
3.2.1 继承的要点
(1)父类也称作超类、基类、派生类等。
(2)Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。
(3)Java中类没有多继承,接口有多继承。
(4)子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父 类私有的属性和方法)。
(5)如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object。
3.2.2 继承的实现
(1)方法重写(override)
方法的重写需要符合下面的三个要点:
a.“= =”: 方法名、形参列表相同。
b.“≤”:返回值类型和声明异常类型,子类小于等于父类。
b.“≥”: 访问权限,子类大于等于父类。
(2)代码实现
public class Animal {
public void shout(){
System.out.println("叫一声");
}
}
class Dog extends Animal{
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal{
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
注意:
继承的层次不可太多,尽量两到三层;继承的最上层最好抽象
3.2.3 final 关键字
final关键字的作用:
(1)修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
final int MAX_SPEED = 120;
(2)修饰方法:该方法不可被子类重写。但是可以被重载!
final void study(){}
(3)修饰类: 修饰的类不能被继承。比如:Math、String等。
final class A {}
3.2.4 组合
我们可以通过继承方便的复用已经定义类的代码。还有一种方式,也可以方便的实现“代码复用”,那就是:“组合”。
“组合”不同于继承,更加灵活。
“组合”的核心就是“将父类对象作为子类的属性”,然后,“子类通过调用这个属性来获得父类的属性和方法”。
示例如下:
public class Test{
public static void main(String[ ] args) {
Student s = new Student("高淇",172,"Java");
s.person.rest(); //s.rest();
s.study();
}
}
class Person {
String name;
int height;
public void rest(){
System.out.println("休息一会!");
}
}
class Student /*extends Person*/ {
Person person = new Person();
String major; //专业
public Student(String name,int height,String major) {
//天然拥有父类的属性
this.person.name = name; //this.name = name;
this.person.height = height; //this.height = height;
this.person.rest();
this.major = major;
}
}
3.3 多态(polymorphism)
3.3.1 多态的要点
a.多态是方法的多态,不是属性的多态(多态与属性无关)。
b. 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。
c. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
3.3.2 对象转型
父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。
向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型!
3.3.3 instanceof关键字
instanceof是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回true;否则,返回false。
3.3.4代码实现
public class Animal {
public void shout(){
System.out.println("叫一声");
}
}
class Dog extends Animal{
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal{
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
class TestExtends{
public static void main(String[] args) {
Animal a = new Dog();
a.shout();
if (a instanceof Dog){
Dog d1 = (Dog) a;
a.shout();
}
if (a instanceof Cat){
Cat c1 = (Cat) a;
c1.shout();
}
}
}
4.抽象类(abstract)
4.1 抽象方法和抽象类
抽象方法:使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。
抽象类:包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
4.2使用要点
(1)有抽象方法的类只能定义成抽象类
(2)抽象类不能实例化,即不能用new来实例化抽象类。
(3)抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用 来被子类调用。
(4)抽象类只能用来被继承。
(5)抽象方法必须被子类实现。
4.3代码实现
//抽象类
abstract class Animal {
abstract public void shout(); //抽象方法
}
class Dog extends Animal {
//子类必须实现父类的抽象方法,否则编译错误
public void shout() {
System.out.println("汪汪汪!");
}
public void seeDoor(){
System.out.println("看门中....");
}
}
//测试抽象类
public class TestAbstractClass {
public static void main(String[ ] args) {
Dog a = new Dog();
a.shout();
a.seeDoor();
}
}
5.接口(interface)
5.1接口作用
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。
抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。
5.2 声明
声明格式:
[访问修饰符] interface 接口名 [extends 父接口1,父接口2…] {
常量定义;
方法定义;
}
定义接口的详细说明:
(1)访问修饰符:只能是public或默认。
(2)接口名:和类名采用相同命名机制。
(3)extends:接口可以多继承。
(4)常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
(5)方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract。
5.3代码实现
class TestInterface{
public static void main(String[] args) {
Volant volant = new Angel();
volant.fly(); //Angel类只实现了Volant接口的fly(),不能调用Angel类中实现Honest的helpOther()
Honest h= (Honest) volant;//要想调用,需要强转成Honest,这样编译才能通过
h.helpOther();
System.out.println(Volant.FLY_HIGHT);
Honest h2= new GoodMan();
h2.helpOther();
Volant v2= new SuperMan();
v2.fly();
}
}
//飞行接口
interface Volant {
public static final int FLY_HIGHT =100;
void fly();
}
//善良接口
interface Honest{
void helpOther();
}
class Angel implements Volant,Honest{
@Override
public void fly() {
System.out.println("我是天使,会飞");
}
@Override
public void helpOther() {
System.out.println("我是天使,很善良,正在扶老奶奶过马路");
}
}
class GoodMan implements Honest{
@Override
public void helpOther() {
System.out.println("我是一个好人,正在铲奸除恶");
}
}
class SuperMan implements Volant{
@Override
public void fly() {
System.out.println("我是超人,我会飞");
}
}
5.4 默认方法和静态方法
JAVA8之前,接口里的方法要求全部是抽象方法。
JAVA8(含8)之后,以后允许在接口里定义默认方法和类方法。
public interface A {
default void moren(){
System.out.println("我是接口A中的默认方法");
}
static void jingtai(){
System.out.println("我是接口A中的静态方法");
}
}
class Test_A implements A{
@Override
public void moren() {
System.out.println("我是实现类中重写的默认方法");
}
void jingtai(){
System.out.println("我是子类里面的静态方法(不是重写[静态方法不能重写],是新方法)");
}
public static void main(String[] args) {
A a= new Test_A();
a.moren();
Test_A t1= (Test_A) a;
t1.jingtai();
A.jingtai();
}
}
5.5接口的多继承
interface A {
void testa();
}
interface B {
void testb();
}
/**接口可以多继承:接口C继承接口A和B*/
interface C extends A, B {
void testc();
}
public class Test implements C {
public void testc() {
}
public void testa() {
}
public void testb() {
}
}
6.垃圾回收机制(Garbage Collection)
Java引入了垃圾回收机制,令C++程序员最头疼的内存管理问题迎刃而解。Java程序员可以将更多的精力放到业务逻辑上而不是内存管理工作上,大大的提高了开发效率。
分代垃圾回收机制,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。我们将对象分为三种状态:年轻代、年老代、持久代。同时,将处于不同状态的对象放到堆中不同的区域。 JVM将堆内存划分为 Eden、Survivor 和 Tenured/Old 空间。
- 年轻代
所有新生成的对象首先都是放在Eden区。 年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象,对应的是Minor GC,每次 Minor GC 会清理年轻代的内存,算法采用效率较高的复制算法,频繁的操作,但是会浪费内存空间。当“年轻代”区域存放满对象后,就将对象存放到年老代区域。 - 年老代
在年轻代中经历了N(默认15)次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。年老代对象越来越多,我们就需要启动Major GC和Full GC(全量回收),来一次大扫除,全面清理年轻代区域和年老代区域。 - 永久代
用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响。JDK7以前就是“方法区”的一种实现。JDK8以后已经没有“永久代”了,使用metaspace元数据空间和堆替代。
图 堆内存的划分细节
(1)Minor GC:用于清理年轻代区域。Eden区满了就会触发一次Minor GC。清理无用对象,将有用对象复制到“Survivor1”、“Survivor2”区中。
(2)Major GC:用于清理老年代区域。
(3)Full GC:用于清理年轻代、年老代区域。 成本较高,会对系统性能产生影响。