目录
类,对象,引用
类
类是具有相同属性和行为的对象的集合。
类定义了该类型对象的数据结构, 称之为
“
成员变量
”
,同时也定义了一些可以被调用的功能,称之
为
“
方法
”
。
类是用于构建对象的模板,对象的实质就是内存中的一块存储区域,其数据结构由定义它的类来决
定。
定义类
类的成员变量
类的成员方法
对象
当一个类的定义存在后,可以使用new运算符创建该类的对象。
对象创建的过程一般称为类的实例化。
成员变量的初始化
对象创建之后,其成员变量可以按照默认的方式初始化,对象成员具有默认值。
成员变量的默认初始化值规则
成员变量的调用
成员变量的调用,可以根据某个对象的引用,找到成员变量,然后使用成员变量
// 1. 创建一个汽车
Car car = new Car() ; // 创建的对象,赋值给引用类型的变量。
// 2. 给汽车的属性赋值
car.pinPai = "BYD宋";
car.color = "红色";
car.faDongJi = "byd发动机";
car.count = 7 ;
成员方法的调用
方法的调用必须通过某个对象的引用。
当通过么对象的引用调用方法时,方法中涉及的成员变量就是该对象的成员变量。
Person p1 = new Person();
p1.eat("火锅");
p1.sayHello();
p1.driver();
引用类型
- 除8种基本数据类型之前,用类名(接口,数组)声明的变量称为引用类型变量,简称引用。
- 引用类型变量中存储的是某个对象在内存中的地址信息。
- 引用的功能在于访问对象。
- 引用类型变量声明语法规则:
类名 引用类型变量名;
JVM内存结构
方法区
该区间用于存放类的消息。Java程序运行时候,首先会通过类加载器载入类文件的字节码文件,经 过解析后将其装入方法区。类的各种信息都在方法区保存。
栈内存区
栈用于存放程序运行过程中的局部变量。
引用类型的变量
p ,
存储在栈内存中。
堆内存区
jvm
会在其内存空间开一个称为
“
堆
”
的存储空间,这部分空间用于存储使用
new
关键字创建的对
象。
创建了一个
Person
对象,存储在堆内存中。
方法的重载
在
java
语言中,允许多个方法的名称相同,但参数列表不同,称之为方法的重载
(overload)
。
编译器在编译时,会根据其参数的不同,绑定到不同的方法。
this
在方法中可以通过
this
关键字表示调用该方法的对象。
通常在类中使用
this
区分成员变量和参数,如果没有歧义,可以省略
this
。
一个类可以创建多个对象
(
存储于堆中
)
,但方法只有一份
(
存储于方法区中
)
。
null
引用类型变量用于存放对象的地址,可以给引用类型赋值为
null,
表示不指向任何对象。
如果某个引用类型变量为
null
的时候,不能访问对象,否则抛出空指针异常。
构造函数
定义构造方法
- 构造方法是在类中定义的方法(每个类都有构造方法):
构造方法的名称必须和类名相同.
构造方法没有返回值,但也不能写void. - 在java语言中可以通过构造方法实现对象成员变量初始化。
构造方法重载
为了使用方便对一个类定义多个构造方法,这些构造方法彼此参数不同,称为构造方法的重载。 创建对象时,java编译器会根据不同的参数,选择不同的构造函数。
继承
extends
关键字可以实现类的继承
子类(
sub class
) 可以继承父类(
super class
)的成员变量及成员方法。同时也可以定义自己的
成员变量和成员方法。
java
语言不支持多继承,一个类只能继承一个父类。 但一个父类可以有多个子类。
向上造型
一个子类的对象可以向上造型为父类的类型
Father f = new Sun();
java
编译器编译的时候根据父类型检查调用的成员变量和成员方法是否匹配。
instanceof关键字
对于一个父类的引用类型,可以指向该类的对象也可以指向其任意一个子类型的对象。
通过
instanceof
关键字判断引用指向的对象的实际类型。
根据引用指向的实际类型,将引用强制转换为实际类型
继承中的构造方法
子类的构造方法中必须通过
super
调用父类的构造方法。因为创建子类之前,必须先创建父类。
子类的构造函数如果没有直接使用
super
调用父类构造方法,
java
编译器会自动的加入对父类无参
构造函数的调用(那么要求父类必须有无参构造方法)。
方法重写
子类从父类继承的方法,如果不能满足子类的需要,可以重写父类的方法。即方法名和参数列表和
父类保持完全一致,方法的实现不同。
子类重写父类的方法之后,子类对象优先调用自己的方法。
子类重写父类的方法的时候,可以在重写的方法中,使用
super
关键字调用到父类的方法。
// 图形
public class Shape {
int x ;
int y ;
public double area(){ // 成员函数
System.out.println("Shape的area函数被调用");
return 0 ;
}
}
class Rect extends Shape{
double w ;
double h ;
方法重载之后,子类调用自己重写之后的方法
6.package 关键字
定义类的时候需要指定类的名称。但是如果仅仅将类名作为类的唯一标志,则会出现命名冲突的问
题。
在java中,是用package 来解决命名冲突的问题。因此定义类的时候,一般需要先给类指定一个包
名。
类的真实名字是: 包名 + 类名。
包名可以有层次结构,一个类可以有多层包名。
// 重写:方法签名(方法的修饰符 , 返回值类型, 方法名,参数等)和父类的方法完全一致。
public double area(){
System.out.println("Rect中的area函数被调用。");
return w * h ;
}
public double area(double w , double h ){
return w * h ;
}
}
方法重载之后,子类调用自己重写之后的方法
public static void main(String[] args) {
// 1. 创建一个矩形对象
// ** 先创建父类对象,然后在创建子类对象。
Rect rect = new Rect();// 通过super() , 调用父类的无参构造函数:
// 通过super(x , y); 调用父类的无参构造函数:
Rect rect1 = new Rect(3, 4) ;
// 通过super(); 调用父类的无参构造函数:
Rect rect2 = new Rect(3, 4, 10, 10);
// 2. 调用成员方法
double res = rect2.area();
System.out.println("res:" + res);
Shape s = new Shape( );
s.area() ;// s指向的是Shape对象,调用Shape的area方法。
Shape s1 = new Rect();
s1.area() ;// 编译期s1是Shape, Shape中有area,所以没有编译错误。
// 运行的时候s1是Rect类型,矩形有自己的area方法,
// 所以调用Rect的area方法。
}
package关键字
- 定义类的时候需要指定类的名称。但是如果仅仅将类名作为类的唯一标志,则会出现命名冲突的问题。
- 在java中,是用package 来解决命名冲突的问题。因此定义类的时候,一般需要先给类指定一个包名。
- 类的真实名字是: 包名 + 类名。
- 包名可以有层次结构,一个类可以有多层包名。
-
如果各个公司和组织的程序员都随意命名包,并不能很好的解决命名冲突问题。因此指定包名的时候建议使用:公司域名反写 + 项目名 + 项目模块名 + mvc 模式分层
import关键字
如果要在程序中,使用某个类,可以用该类的全名,这样比较复制。
java.util.Date date = new java.util.Date();
一般使用
import
语句导入这个类,然后使用该类
访问控制符
- private修饰的成员变量和方法仅仅只能在本类中调用,因此private修饰的内容是对内实现数据的封装,如果“公开”会增加维护的成本。
- public修饰的成员变量和方法可以在任何地方调用,因此public修饰的内容是对外可以被调用的功能。
- 在定义类的时候,一般采用成员变量私有化,方法公开的原则。
- 用protected修饰的成员变量和方法可以被子类及同一个包中的类使用。
- 默认访问控制即不书写任何访问控制符,默认访问控制的成员变量和方法可以被同一个包中的类调用。
return关键字
java
中规定方法必须要指定返回值类型,如果没有返回值类型,可以使用
void
关键字。
java
中使用
return
关键字,在方法的内部返回数据。
定义方法的语法规则:
方法的修饰符
方法的返回值
方法的名字(方法的参数)
{
方法体
}
static关键字
修饰成员变量
- 用static修饰的成员变量不属于对象的数据结构
- static修饰的变量属于类的变量,通常可以通过类名来引用static成员
- static成员变量和类的信息一起存储在方法区,而不是在堆内存中
- 一个类的静态成员变量只有一份,而非静态成员对象有多份,即每个对象都有自己的非静态成员变量。
修饰成员方法
- 类中的方法,通常都会涉及到堆具体对象的操作,这些方法在调用时,需要隐式的传递对象的引用。
void print(){
System.out.println(this.x + "," + this.y);
} - static修饰的方法则不需要针对对象进行操作,其运行结构仅仅与输入的参数有关系。调用时候直接通过类名引用。
Math.sqrt(100) - static修饰的方法是属于类的方法,通常用于提供工厂方法,或者工具方法。
- static修饰的方法,调用的时候没有具体的对象,因此static修饰的方法不能调用非静态成员变量和成员方法。
final关键字
修饰类
final
关键字修饰的类不可以被继承
对于一些类,设置了
final
修饰,可以避免滥用继承造成的危害
修饰方法
final
关键字修饰的方法可以不被重写
防止子类在定义自己的方法的时候,不经意被重写。
修饰成员变量
final
修饰成员变量,意为不可以改变。
该成员变量需要在初始化的时候赋值,对象一旦创建,即该值不可以改变。
对象数组
所谓对象数组,对象数组的元素不是基本类型,而是引用类型
对象数组的初始化和基本类型数组的初始化方式一样,只不过元素是对象引用而已。
对象数组创建之后,元素的默认值是
null
二维数组
数组的元素可以为任何类型,也包括数组类型。
二维数组: 数组的元素如果是一个数组,那么这个数组就被称为二维数组。
int[][] = {{1,3,2},{4,5,6}}
Point[][] ps = {{new Point(1,2),new Point(1,2)},{new Point(1,2),new
Point(1,2)}}
//如果每个数组的元素长度相同:
int[][] = new int[3][5]
二维数组的使用
package oopday3;
import java.util.Arrays;
// 二维数组: 数组中的元素依然是一个数组,就被称为二维数组。
public class Array2Demo {
public static void main(String[] args) {
// 基本数据类型的2维数组 :
int[][] arr = new int[5][];// 定义二维数组的时候,需要设置第一维长度。
arr[0] = new int[5]; // 数组的第一个元素,是一个长度为5的整形数组。
arr[1] = new int[5];
arr[2] = new int[4];
arr[3] = new int[4];
arr[4] = new int[4];// 由于数组中的元素的长度不一致,所以呢,定义这个二维数组
的时候,只能确定第一维的长度,第二维的长度由每个元素决定。
// 遍历二维数组: 循环的嵌套
// 把数组的每个元素输出在一行显示
for(int i = 0 ; i < arr.length ;i ++){
// arr[i] 就是数组中的每个元素(这个元素还是数组,素以呢就继续使用for循环,遍
历这个元素)
for(int j = 0 ; j < arr[i].length ; j ++){
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}
System.out.println("_________________________________");
// 定义一个长度是5的数组,数组的元素还是一个长度为5的数组。
int[][] arr1 = new int[5][5];
// 遍历数组
for(int i=0 ; i < arr1.length ; i++){
System.out.println(Arrays.toString(arr1[i]));
}
// 二维数组初始化
System.out.println("_________________________________");
String[][] strs = {{"A","A","A","A"},{"B","B","B","B"},
{"C","C","C","C"}};
for(int i=0 ; i < strs.length ; i++){
System.out.println(Arrays.toString(strs[i]));
}
// 二维数组定义
Point[][] ps = {{new Point(1,2) , new Point(1,2)} ,
{new Point(1,2) , new Point(1,2)},
{new Point(1,2) , new Point(1,2)},
{new Point(1,2) , new Point(1,2)}};
System.out.println("------------------------");
for(int i=0 ; i < ps.length; i ++){
System.out.println(ps[i][0].x +"," +ps[i][0].y +"\t" + ps[i]
[1].x + ","+ ps[i][1].y );
}
}
}
抽象类
用
abstract
关键字修饰的类称之为抽象类。
抽象类不能实例化,抽象类的意义在于
“
被继承
”
。
抽象类为子类
“
抽象
”
出公共部分,通常也定义了子类所必须实现的抽象方法。
一个抽象类可以有抽象方法,也可以有非抽象方法
一个类继承了抽象类,那么必须实现父类的抽象方法,除非该类依然定义为抽象类。
抽象类的使:
package oopday3;
import java.security.AlgorithmConstraints;
// abstract : 抽象, java关键字abstract ,用于修饰类,表示这个类是一个抽象类。
// *** 抽象类中,可以定义抽象方法。
public abstract class Shape {
int x ;
int y ;
/*
public double area(){ // 实现了的方法。 这里并不能体现出面积的计算公式 ,因为当前
这个类型不太方便计算面积。
return 0;
}
*/
public abstract double area();// 这个是一个抽象方法, 只是定义了方法,并没有实
现方法。
// 定义了一个规范,那就是所有Shape类型的子类,都
必须实现area方法。
}
class Circle extends Shape{ // 继承了抽象类之后, 必须要重写父类的抽象方法。
int r ; // 半径
public double area(){ // 方法的实现 。
return Math.PI * r * r ; // 计算面积的公式。
}
}
class Rect extends Shape{
double w;
double h;
public double area(){// 继承了抽象类之后, 必须要重写父类的抽象方法。
return w * h;
}
}
/* 编译错误: 非抽象类中,不能出现抽象方法。
class Other{
public abstract double area();
}
*/
/* 抽象类中,可以有抽象方法,也可以没有抽象方法。
abstract class Other{
public double area(){
return 10 ;
}
}
*/
// 如果父类有抽象方法,子类要么实现这些抽象方法,要么自己依然定义为抽象类。
abstract class SanJiao extends Shape{
}
接口
接口可以看成是特殊的抽象类。
接口中只能定义常量和抽象方法。
一个类可以实现多个接口,多个接口之间用逗号分隔。这个类需要实现所有接口的抽象方法。
接口可以作为一种类型声明变量,一个接口类的变量可以引用实现了该类的对象。通过该引用,调
用接口中的方法(实现类中提供了接口方法的实现)
接口间可以存在继承关系,一个接口可以通过
extends
关键字继承另外一个接口。
子接口继承了父接口中定义的所有方法。
接口的使用
package oopday4;
// interface - 接口: 接口用于制定规范,java中的接口类型中,只能写常量的定义, 抽象方
法的定义。
// 定义好接口之后, 可以通过类实现接口 ,然后类中重写这些接口中定义的方法。
// 接口是抽象的,不能创建对象。
public interface Animal {
// 定义常量
static final String OLDER = "女娲";
// 定义抽象方法。
void eat(); // 接口中的方法默认是public abstract . 所以可以省略这两个关键字。
public abstract void sleep();
public abstract void sing();
}
// Cat实现了Animal接口, 所以Cat是Animal.
class Cat implements Animal{// 类需要实现接口中的所有抽象方法, 否则把类设置为
abstract类。
String pinZhong;
String name;
@Override
public void eat() {
System.out.println("吃东西");
}
@Override
public void sleep() {
System.out.println("睡觉");
}
@Override
public void sing() {
System.out.println("喵喵叫....");
}
public void catchMouse(){
System.out.println("抓老鼠");
}
}
一个类继承多个接口
// Intro ,定义一个打印成员变量的方法。
// Intro制定了一个规范, 实现Intro接口的类,必须提供一个print方法。
public interface Intro {
void print();
}
// 一个类可以实现多个接口。那么这个类需要实现每个接口中定义的方法
class Mouse implements Animal , Intro {
String name;
String color;
@Override
public void eat() {
System.out.println("老鼠在偷吃东西。");
}
@Override // 空实现: 语法规则是有{}的 , 但是{}内部没有代码。
public void sleep() {
}
@Override
public void sing() {
}
@Override
public void print() {
System.out.println(this.name + "是" + this.color);
}
}
子接口继承父接口
// ** java中规定extends 是单继承(一个类只能extends一个父类)。
// java中可以一个类同时实现多个接口。 (一个类能够 implements 多个接口。)
// java中的类可以继承父类,同时实现某些接口。
//Tiger 继承了Animal中的eat , sleep , sing等方法。
// ** 因为Tiger 依然是一个interface类型, 所以这里不重写父接口中的方法。
public interface Tiger extends Animal {
public abstract void milk(); // Tiger具有的方法
}
class HuaNan implements Tiger{
@Override
public void eat() {
}
@Override
public void sleep() {
2.内部类
}
@Override
public void sing() {
}
@Override
public void milk() {
}
}
class DongBeiTiger implements Tiger{
@Override
public void eat() {
}
@Override
public void sleep() {
}
@Override
public void sing() {
}
@Override
public void milk() {
}
}
// HeiLoingJiangTiger :可以是 DongBeiTiger , Tiger , Animal , Intro.
class HeiLoingJiangTiger extends DongBeiTiger implements Intro{
@Override
public void print() {
}
}
内部类
一个类可以定义在另外一个类的内部,定义在内部的类称为内部类
,
其所在的类称之为外部类。
定义在内部的类,一般只服务于其外部类,堆外部类具备可见性,内部类可以直接使用外部类的成
员及方法。
通常内部类不能独立于外部类存在,一般设置内部类为
private
。在外部类之前创建内部类对象,如
下语法规则:
当一个类存在的价值仅仅为某一个类服务时,应使其成为那个类的内部类。
内部类一般情况下堆外不可见,除了包含它的外部类外,其他类无法访问到它。
内部类可以很方便的访问其外部类的私有属性
静态内部类
使用static修饰的成员内部类称为静态内部类,在外部类加载时存在
匿名内部类
如果在一段程序中需要创建一个类的对象(通常这个类需要实现某个接口或者继承某个类),而且
对象创建后,这个类的价值也就不存在了,这个类可以不必命名,称之为匿名内部类。
局部内部类
定义在方法中的类,称为局部内部类。
值传递&引用传递
- 如果一个方法的参数为引用类型,直接修改该参数会对其造成影响。
- 如果一个方法的参数为引用类型,该方法中又创建了新对象,不会对实际参数造成影响。
- 如果一个方法的参数为字符串,该方法中赋值了一个新字符串,不会对实际参数造成影响
- 如果一个方法的参数为原始类型,该方法不会对实际参数造成影响。
== 和equals的区别
- == , 基本数据类型,判断数据值是否相等
- == , 引用类型,判断引用类指向的地址是否相等,即是否为同一个对象。
- equals是父类Object中提供的的一个equals方法,用于制定两个对象是否相等的比较规则。 Object中的equals默认是比较两个对象的地址是否相同。
子类重写父类的equals方法,自定义比较规则:
比如: 判断两个点是否相同,可以判断两个点的x位置和y位置同时相等,则两个点相同。 - 如果两个对象比较equals为true ,那么这依然不能说明是否为同一个对象。
- 重写类的equals方法: 使用idea的自动生成
面向对象编程&面向过程编程
- Object Oriented Programming 简称OOP: c++ ,java , c#
- Procedure Oriented Programming 简称POP: c语言
- 面向过程,侧重于分析问题的步骤和具体细节,亲力亲为地去完成每一步都是基于上一步去完成
- 面向对象,侧重于创建解决问题的实体,通过实体互相配合去完成每个实体解决问题的细节实质也是面向过程的
面向对象三大特性
封装
- 通过private关键字对成员变量进行修饰,提供公开的getter和setter方法用于简单地对成员变量进行取值和赋值。
- 可以隐藏具体的细节,实现代码安全
- 在Idea中,自动生成getter/setter的快捷键为:
右键--generate或alt+insert -->getter and setter
选择要生成的成员变量
继承
- A类通过extends关键字,继承B类,Java是中是单继承,一个子类只能有一个父类,一个父类可以 有多个子类
- 继承能够提高代码的重用性
- Java是中是单继承,一个子类只能有一个父类,一个父类可以有多个子类
多态
- 对象是多种状态的。
- 父类的引用指向子类的对象
Father s=new Son();
静态代码块
// 静态代码块
// 非静态代码块
public class CodeBlockDemo {
private int x ;
static double pi;
static { //静态代码块, 类加载完成的时候,执行静态代码块。
System.out.println("这个是静态代码块");
pi = 3.14;
}
{ // 非静态代码块 , 创建对象的时候,执行非静态代码块。
System.out.println("非静态的代码块");
this.x = 10 ;
}
public CodeBlockDemo(){
System.out.println("对象创建成功了。");
}
// 要求: 类加载的时候,给变量pi初始化为3.14 . 对象创建的时候,给变量x ,初始化为10.
public static void main(String[] args) {
CodeBlockDemo cbd = new CodeBlockDemo();
System.out.println(CodeBlockDemo.pi);
System.out.println(cbd.getX());
CodeBlockDemo cbd1 = new CodeBlockDemo();
System.out.println(cbd1.getX());
}
// 成员方法
public void add(){
x++;
}
public void add(int k){
this.x = x*k;
}
// get & set
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
// equals & hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CodeBlockDemo)) return false;
CodeBlockDemo that = (CodeBlockDemo) o;
return getX() == that.getX();
}
@Override
public int hashCode() {
return Objects.hash(getX());
}
}