1. 类 , 对象, 引用
1.1 类
类是具有相同属性和行为的对象的集合。
类定义了该类型对象的数据结构, 称之为“成员变量”,同时也定义了一些可以被调用的功能,称之为“方法”。
类是用于构建对象的模板,对象的实质就是内存中的一块存储区域,其数据结构由定义它的类来决
定。
定义类
类的成员变量
类的成员方法
public class Point {
//类型的特征(属性,成员变量)
int x;//横坐标
int y;//纵坐标
//功能(方法,能做什么)
public void up(){//public公开的方法 void表示无返回值 up()表示无参。
y++;//纵坐标加一。
}
public void up(int y){//方法重载了
//给成员变量y赋值
//y=y;//自己赋值给自己
this.y = y;//用this来指代正在被操作的对象,然后访问到对象的成员变量。
}
}
//这个point代表的是一个数据类型,不代表一个具体的点。--------类
//我想表示x=5 y=6的点 这里就是一个具体的点。---------对象
// new Point() , 然后设置x=5,y=6 这一句创建的对象没有赋值给一个同类型的变量,导致无法找到这个对象,然后修改不了x,y的值
// Point p1 ,这里的p1是一个Point类型的变量名-------引用
// Point p1=new Point(); 这里表示把new出来的Point对象,赋值给同类型的引用量p1,然后通过p1操作对象的属性和方法。
1.2 对象
当一个类的定义存在后, 可以使用 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();
1.3引用类型
除8种基本数据类型之前,用类名(接口,数组)声明的变量称为引用类型变量,简称引用。
引用类型变量中存储的是某个对象在内存中的地址信息。
引用的功能在于访问对象。
引用类型变量声明语法规则:
2. JVM内存结构
2.1 方法区
该区间用于存放类的信息。java程序运行时候,首先会通过类加载器载入类文件的字节码文件,经过解析后将其装入方法区。类的各种信息都在方法区保存。
2.2 栈内存区
栈用于存放程序运行过程中的局部变量。
引用类型的变量p ,存储在栈内存中。
2.3 堆内存区
jvm会在其内存空间开一个称为“堆”的存储空间,这部分空间用于存储使用new关键字创建的对象。
创建了一个Person对象,存储在堆内存中。
3.方法的重载
在java语言中,允许多个方法的名称相同,但参数列表不同,称之为方法的重载(overload)。编译器在编译时,会根据其参数的不同,绑定到不同的方法。
4.this
在方法中可以通过this关键字表示调用该方法的对象。
通常在类中使用this区分成员变量和参数,如果没有歧义,可以省略this。
一个类可以创建多个对象(存储于堆中),但方法只有一份(存储于方法区中)。
public class TestPoint {
public static void main(String[] args) {
//1.创建对象
Point p1 = new Point();//把创建好的对象,赋值给p1,就是p1这个引用指向Point对象
System.out.println("("+p1.x+","+p1.y+")");
//2.修改对象的数据
p1.x=5;
p1.y=6;
System.out.println("("+p1.x+","+p1.y+")");
//3.调用方法,移动点
p1.up();//上移动一个
System.out.println("("+p1.x+","+p1.y+")");
//4.调用up方法,让y移动到指定的位置。
p1.up(10);//移动到纵坐标为10的地方了。
System.out.println("("+p1.x+","+p1.y+")");
//4.调用up方法,让y移动到指定的位置
p1.up(10);//移动到纵坐标为10的地方了。
}
}
5.null
引用类型变量用于存放对象的地址,可以给引用类型赋值为null,表示不指向任何对象。
如果某个引用类型变量为null的时候,不能访问对象,否则抛出空指针异常。
6. 构造函数
6.1定义构造方法
构造方法是在类中定义的方法(每个类都有构造方法):
— 构造方法的名称必须和类名相同.
— 构造方法没有返回值,但也不能写void.
在java语言中可以通过构造方法实现对象成员变量初始化。
6.2构造方法重载
为了使用方便对一个类定义多个构造方法,这些构造方法彼此参数不同,称为构造方法的重载。
创建对象时,java编译器会根据不同的参数,选择不同的构造函数。
7.继承
extends关键字可以实现类的继承
子类(sub class) 可以继承父类(super class)的成员变量及成员方法。同时也可以定义自己的
成员变量和成员方法。
java语言不支持多继承,一个类只能继承一个父类。 但一个父类可以有多个子类。
import java.util.Date;
// 动物类型: 公共的成员变量 , 公共的成员方法
// 颜色 , 生日, 性别 , 脚的数量 , 叫 , 吃 , 跑
// 鸡类 : 飞 ,
// 狗类 : 看门 ,
// ** java规定,一个java文件只能有一个公开类 , 并且这个公开类的名字必须和java文件名相
同。
public class Animal {
String color ;// 颜色
Date birth ;// 生日
String sex ;// 性别
int count ;// 脚的数量
public void sing(String sing){
System.out.println("唱:" +sing);
}
public void eat(String food){
System.out.println("吃:" + food);
}
public void run(){
System.out.println("到处跑");
}
}
/*Chicken 这个类型,只有一个成员方法。
class Chicken{
public void fly(){
System.out.println("飞....");
}
}
*/
// Chicken 继承了Animal中的所有成员变量和成员方法, 同时有自己独有的fly方法。
class Chicken extends Animal{
public void fly(){
System.out.println("飞......");
}
}
// Dog 继承了Animal中的所有成员变量和成员方法, 同时有自己独有的seeDoor方法。
class Dog extends Animal{
public void seeDoor(){
System.out.println("保护家园.......");
}
}
8.向上造型
一个子类的对象可以向上造型为父类的类型
Father f = new Son();
java编译器编译的时候根据父类型检查调用的成员变量和成员方法是否匹配。
// 3. 向上造型 : 子类对象,赋值给父类的引用
Animal dog = new Dog() ;//
dog.birth = new Date();
dog.color = "黄色";
dog.count = 4 ;
dog.sex = "女";
dog.sing("汪汪。。。。");
dog.eat("狗粮......");
dog.run();
// dog.seeDoor() ;// 编译错误: 在编译的时候, dog引用是按引用的类型Animal使
用。 Animal没有seeDoor的方法
// 在运行的时候, dog引用找到的是对象的实际类型Dog ,进行使
用,这个时候就有seeDoor方法。
// 如果一定要调用seeDoor() , 这里只能修改dog的引用类型为Dog. ,即: Dog dog
= new Dog();
8.instanceof关键字
对于一个父类的引用类型,可以指向该类的对象也可以指向其任意一个子类型的对象。
通过instanceof 关键字判断引用指向的对象的实际类型。 根据引用指向的实际类型,将引用强制转换为实际类型。
// 4. 创建Animal : 动物是动物 , 狗是动物 , 鸡是动物。
Animal a1 = new Animal() ;
Animal a2 = new Dog(); // Dog 和Chicken都是Animal的子类。
Animal a3 = new Chicken() ;
// 5. 类型转换 :
// instanceof : 用于判断 引用 对应的对象是否为某种类型 , 是结果为true ,否
则结果为false.
// a2是动物 , 但是a2本质是狗。 a2不能转换为鸡这个类型。
if(a2 instanceof Dog){ // instanceof的运算结果是true或者false.
// a2 是 Dog , 那么就可以把a2转换为Dog ,然后赋值给Dog类型的引用.
Dog dog1 = (Dog) a2;
dog1.seeDoor(); // 类型转换之后,就可以调用到子类Dog的所有方法。
}
if(a3 instanceof Chicken){
Chicken chicken = (Chicken) a3;
chicken.fly();
}
Chicken chicken = (Chicken) a2; // 没有编译错误。 有运行错误:
ClassCastException - 类型转换错误。
/*
Exception in thread "main" java.lang.ClassCastException: oopday2.Dog
cannot be cast to oopday2.Chicken
at oopday2.TestAnimal.main(TestAnimal.java:51)
*/
9.继承中的构造方法
子类的构造方法中必须通过super调用父类的构造方法。因为创建子类之前,必须先创建父类。
子类的构造函数如果没有直接使用super调用父类构造方法,java编译器会自动的加入对父类无参 构造函数的调用(那么要求父类必须有无参构造方法)。
import javafx.beans.binding.DoubleExpression;
// 图形
public class Shape {
int x ;
int y ;
public Shape(){ // 无参构造器函数
System.out.println("Shape的无参构造函数被调用");
}
public Shape(int x , int y){
this.x = x ;
this.y = y ;
System.out.println("Shape的有参构造函数被调用");
}
public double area(){ // 成员函数
System.out.println("Shape的area函数被调用");
return 0 ;
}
}
class Rect extends Shape{
double w ;
double h ;
public Rect(){
super(); // 调用父类的无参构造函数 , 如果要写只能写在构造器函数内部的第一行。
//super(); -- 构造器函数内部,会调用父类型的构造器函数。 如果没有写super() ;
那么编译器会主动添加super().
System.out.println("Rect的无参构造函数被调用");
}
public Rect(int x , int y ){
super(x , y); // 这里就表示调用父类型的有两个参数的构造器函数。
System.out.println("Rect的有两个参构造函数被调用");
}
public Rect(int x , int y , double w , double h){
// 第一行这里隐藏了super() .
this.x = x;
this.y = y ; // this.x = x ; this.y = y 这里两句可以简化为super(x, y)
this.w = w ;
this.h = h ;
System.out.println("Rect的有四个参构造函数被调用");
}
public Rect(double w , double h){
//super(w,h); // 编译错误: super调用父类的构造器,必须是一个父类存在的。
}
// 重写:方法签名(方法的修饰符 , 返回值类型, 方法名,参数等)和父类的方法完全一致。
public double area(){
System.out.println("Rect中的area函数被调用。");
return w * h ;
}
public double area(double w , double h ){
return w * h ;
}
}
10.方法重写(overwrite)
子类从父类继承的方法,如果不能满足子类的需要,可以重写父类的方法。即方法名和参数列表和父类保持完全一致,方法的实现不同。
子类重写父类的方法之后,子类对象优先调用自己的方法。
子类重写父类的方法的时候,可以在重写的方法中,使用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 ;
// 重写:方法签名(方法的修饰符 , 返回值类型, 方法名,参数等)和父类的方法完全一致。
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方法。
}
11.package 关键字
定义类的时候需要指定类的名称。但是如果仅仅将类名作为类的唯一标志,则会出现命名冲突的问题。
在java中,是用package 来解决命名冲突的问题。因此定义类的时候,一般需要先给类指定一个包名。
类的真实名字是: 包名 + 类名。
包名可以有层次结构,一个类可以有多层包名。
如果各个公司和组织的程序员都随意命名包,并不能很好的解决命名冲突问题。因此指定包名的时
候建议使用:
公司域名反写 + 项目名 + 项目模块名 + mvc模式分层
12.import 关键字
如果要在程序中,使用某个类,可以用该类的全名,这样比较复制。
java.util.Date date = new java.util.Date();
一般使用import 语句导入这个类,然后使用该类。
import java.util.Date;
//import java.sql.Date ;
public class ImportDemo {
public static void main(String[] args) {
//java.util.Date date = new java.util.Date(); // java.util.Date是Date
类的全名。
Date date = new Date(); // 使用import关键字,先导入java.util.Date , 在
类中就可以直接通过类名,使用到这个类型。
java.sql.Date date1 = new java.sql.Date(1);
}
}
13.访问控制符
private修饰的成员变量和方法仅仅只能在本类中调用,因此private修饰的内容是对内实现数据的封装,如果“公开”会增加维护的成本。
public修饰的成员变量和方法可以在任何地方调用,因此public修饰的内容是对外可以被调用的功能。
在定义类的时候,一般采用成员变量私有化,方法公开的原则。
用protected修饰的成员变量和方法可以被子类及同一个包中的类使用。
默认访问控制即不书写任何访问控制符,默认访问控制的成员变量和方法可以被同一个包中的类调
用。
public class Cat {
String name ; // 没有public ,private , protected 的修饰, 所以就是默认的访
问权限。
String sex ;
// 访问权限: public > protected > 默认 > private
public void catchMouse(){
System.out.println(name +"正在抓老鼠。");
}
protected void eat(String food){
System.out.println(name + "吃:" + food);
}
void play(){
System.out.println(name + "陪你玩.....");
}
private void sing(){
System.out.println("喵喵........");
}
}
14.return关键字 :java中方法的定义
java中规定方法必须要指定返回值类型,如果没有返回值类型,可以使用void关键字。
java中使用return关键字,在方法的内部返回数据。
定义方法的语法规则
方法的修饰符 方法的返回值 方法的名字(方法的参数){
方法体
}
代码
// 方法的定义
// *** ① 方法的参数有哪些,什么类型的 ② 方法是否有返回值,如果有是什么类型的
// ③ 方法取一个什么名字(见名知意) ④ 方法的修饰符
// *** return 用于方法中返回一个数据。 return之后的代码都不能被执行。
// 有时候用return 结束方法的运行。
public class MethodDemo {
// 1. 计算矩形的周长,并返回这个数据。 【方法的参数两个,double , 方法有返回值,
double 】
public double girth(double w , double h){
return 2 * ( w + h) ;//
}
// 2. 输出矩形的周长。 【方法有两个参数 ,double, 无返回值】
public void girth1(double w , double h){
System.out.println("周长:" + 2*(w+h));
}
// static : 静态的
public static void main(String[] args) {
// 0. 创建对象
MethodDemo demo = new MethodDemo();
// 1. 调用girth
demo.girth(10,10); // 静态方法内, 通过对象调用对象非静态方法。
// 2. 调用girth1.
demo.girth1(10,1);
}
}
15.static关键字
15.1 修饰成员变量
用static修饰的成员变量不属于对象的数据结构
static修饰的变量属于类的变量,通常可以通过类名来引用static成员
static成员变量和类的信息一起存储在方法区,而不是在堆内存中
一个类的静态成员变量只有一份,而非静态成员对象有多份,即每个对象都有自己的非静态成员变量。
15.2 修饰成员方法
类中的方法,通常都会涉及到堆具体对象的操作,这些方法在调用时,需要隐式的传递对象的引
用。
void print(){ System.out.println(this.x + "," + this.y); }
static修饰的方法则不需要针对对象进行操作,其运行结构仅仅与输入的参数有关系。调用时候直
接通过类名引用。
Math.sqrt(100)
static修饰的方法是属于类的方法,通常用于提供工厂方法,或者工具方法。
static修饰的方法,调用的时候没有具体的对象,因此static修饰的方法不能调用非静态成员变量和
成员方法。
15.3 static的使用
// Cat类型
public class Cat {
// 非静态的成员变量,属于对象。必须要先创建对象,才可以调用这个成员变量。
String name ;
int age ;
// 静态的成员变量,属于类 ,可以通过类名调用静态成员变量。
static int number ; // 代表总共有多少只猫。
public Cat(){
number ++ ;//new 一个猫,就增加一次。
}
public Cat(String name , int age){
this.name = name ;
this.age = age ;
number ++ ;// new 一个猫,就增加一次。
}
public static void print(){ // 静态方法: 属于类, 通过类名直接调用静态方法.
Cat.print() , 这个调用的时候没有对象。
// 方法的内部就不能this。 因为this是指代调用这个方法的
对象。
// System.out.println(this.name +":" + this.age); 如果就是需要输出对象相关的
内容(非静态成员变量) ,那么就只能用非静态的成员方法。
System.out.println("当前总共有:" + number + "只猫");
// sing();// 编译错误: 非静态的方法,不能再静态代码块中使用。
}
public void sing(){ // 非静态的方法,只能通过对象调用这个方法, 就可以使用对象的成员变
量。
System.out.println(this.name +"喵喵.........");
print(); // 可以的: print方法属于类,可以通过对象调用。
}
public static void main(String[] args) {
Cat c1 = new Cat("小红" , 3);
c1.print();
Cat.number = 100 ;// 通过类名访问静态成员变量。
c1.print();
Cat c2 = new Cat("rose" , 2); //number 由100变为101.
c2.print();
Cat.number = 50 ;
c1.print();
}
}
16.final关键字
16.1修饰类
final关键字修饰的类不可以被继承
对于一些类,设置了final修饰,可以避免滥用继承造成的危害
16.2修饰方法
final关键字修饰的方法可以不被重写
防止子类在定义自己的方法的时候,不经意被重写。
16.3修饰成员变量
final修饰成员变量,意为不可以改变。 该成员变量需要在初始化的时候赋值,对象一旦创建,即该值不可以改变。
16.4代码演示
// 测试java中的关键字final : final - 最终 ,修饰类不能被继承,修饰方法不能被重写,修饰成员
变量,赋值之后,值不可变。
public class FinalDemo {
public static void main(String[] args) {
Coo coo = new Coo() ;//
coo.print(); // 调用的是父类中的print方法。
int age = 18 ;
// age是否为一个合法的年龄数据
if(age >= Coo.MIN_AGE && age <= Coo.MAX_AGE){
System.out.println("年龄正确");
}else{
System.out.println("年龄有误");
}
System.out.println(Math.PI); // java中的Math类型定义的常量PI.
}
}
final class Aoo{ // final修饰的类不能被继承。
}
/*
class Boo extends Aoo{ // 编译错误: Aoo是final修饰的类,不能出现子类。
}
*/
class Boo{
public final void print(){ // final修饰的方法,不能被重写。
System.out.println("我是Boo中的print方法。");
}
}
class Coo extends Boo{
int count ;
final int size = 100; // final修饰的变量,定义的时候就赋值,赋值之后,值不可以变。
//size = 120 ;// 编译错误: final修饰的成员变量的值不可以修改了。
// 定义常量: ① 用static和final 一起修饰 [static让这个常量可以通过类名调用。 final让这
个变量的值不变,那就是常量了。]
// ② 建议常量的名字全大写。
static final double PI = 3.14;
// 练习: 最大年龄120 。 最小年龄0.
static final int MAX_AGE = 120 ;
static final int MIN_AGE = 0;
/*
public void print(){ // 编译错误: 父类中的print方法,是final修饰的,不能被重写。
}
*/
}
17.对象数组
所谓对象数组,对象数组的元素不是基本类型,而是引用类型
对象数组的初始化和基本类型数组的初始化方式一样,只不过元素是对象引用而已。
对象数组创建之后,元素的默认值是null.
public class TestPoint {
public static void main(String[] args) {
// 1. 创建5个Point对象,保存在一个变量中。 -- 5个数据,要存在一个变量中, 只有数组
结构符号这个要求。
Point[] ps = new Point[5]; // 创建的数组对象
for(int i = 1; i < 6; i ++){
Point p = new Point(); // 创建对象
p.x = i ;
p.y = i ;
ps[i-1] = p ; // 给数组元素赋值。
}
// 2.修改 ps中的元素的x,y
System.out.println(ps[1].x + ":" + ps[1].y);
ps[1].x = 10 ;
ps[1].y = 10 ;
System.out.println("修改之后:");
System.out.println(ps[1].x + ":" + ps[1].y);
// 3. 输出所有的点的数据
for(int i = 0 ; i < ps.length ; i++){
// System.out.println(ps[i]); // ps[i]输出的是Point的"包名.类名@16进制的整
数"
System.out.println(ps[i].x +"," + ps[i].y);
}
}
}
class Point {
int x ;
int y ;
public Point(){
}
public Point(int x , int y){
this.x = x ;
this.y = y ;
}
}
18.二维数组
数组的元素可以为任何类型,也包括数组类型。
二维数组: 数组的元素如果是一个数组,那么这个数组就被称为二维数组。
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]
二维数组的使用
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 );
}
}
}
19.抽象类
用abstract关键字修饰的类称之为抽象类。
抽象类不能实例化,抽象类的意义在于“被继承”。
抽象类为子类“抽象”出公共部分,通常也定义了子类所必须实现的抽象方法。
一个抽象类可以有抽象方法,也可以有非抽象方法一个类继承了抽象类,那么必须实现父类的抽象方法,除非该类依然定义为抽象类。
抽象类的使用
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{
}
20.接口的使用
接口可以看成是特殊的抽象类。
接口中只能定义常量和抽象方法。
一个类可以实现多个接口,多个接口之间用逗号分隔。
这个类需要实现所有接口的抽象方法。接口可以作为一种类型声明变量,一个接口类的变量可以引用实现了该类的对象。
通过该引用,调用接口中的方法(实现类中提供了接口方法的实现)
接口间可以存在继承关系,一个接口可以通过extends关键字继承另外一个接口。 子接口继承了父接口中定义的所有方法。
接口的使用
// 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() {
}
@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() {
}
}
21.内部类
一个类可以定义在另外一个类的内部,定义在内部的类称为内部类,其所在的类称之为外部类。
定义在内部的类,一般只服务于其外部类,堆外部类具备可见性,内部类可以直接使用外部类的成员及方法。
通常内部类不能独立于外部类存在,一般设置内部类为private。在外部类之前创建内部类对象,如 下语法规则:
当一个类存在的价值仅仅为某一个类服务时,应使其成为那个类的内部类。 内部类一般情况下堆外不可见,除了包含它的外部类外,其他类无法访问到它。 内部类可以很方便的访问其外部类的私有属性 代码
// 内部类:类的内部包含一个其它类,这个被包含的类,就被称为内部类。
// *** 内部类对象的创建,需要依赖于外部类对象。 因此先创建外部类对象,然后借助于外部
类对象,创建内部类。
// 语法规则:外部类的对象.new 内部类();
public class Outer {
private String name ;
private int x ;
private int y ;
public void out(){
System.out.println("我是外部类的out方法。");
}
// 内部类
class Inner{
String name ; // 内部类的成员变量。
String hobby; // 内部类的成员变量。
// 定义一个方法, 用于输出外部类的成员变量的值。
public void print(){
// 访问外部类的成员变量。
// 内部类的成员变量name , 和外部类的成员变量name重名。 优先使用内部类
的成员变量。
// Outer.this.name : 通过外部类的类名.this来访问外部类的成员变
量。
System.out.println(Outer.this.name + ":" + x +":" + y);
System.out.println("内部类的成员变量:" + name + hobby);
// 调用外部类的成员方法。
out();
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.out();// 调用自己类中定义的方法。
outer.x = 10;
outer.y = 20 ;
outer.name = "xxxx";
//Inner in = new Inner(); // 编译错误:Inner是Outer中定义的内部类, 语法
规定需要"外部类的对象.new 内部类()"创建对象。
Inner in = outer.new Inner();
in.name="小红";
in.hobby="喝奶茶";
// 调用内部类的方法。
in.print();
// 链式操作
System.out.println("____________________________");
Inner in1 = new Outer().new Inner();//
in1.print();
}
}
21.1静态内部类
使用static修饰的成员内部类称为静态内部类,在外部类加载时存在
21.2匿名内部类
如果在一段程序中需要创建一个类的对象(通常这个类需要实现某个接口或者继承某个类),而且 对象创建后,这个类的价值也就不存在了,这个类可以不必命名,称之为匿名内部类。
21.3局部内部类
定义在方法中的类,称为局部内部类。
22.值传递&引用传递
如果一个方法的参数为引用类型,直接修改该参数会对其造成影响。
如果一个方法的参数为引用类型,该方法中又创建了新对象,不会对实际参数造成影响。
如果一个方法的参数为字符串,该方法中赋值了一个新字符串,不会对实际参数造成影响
如果一个方法的参数为原始类型,该方法不会对实际参数造成影响。
参考代码
// ** arguments : 参数
public class ArgumentsDemo {
public static void main(String[] args) {
Point point = new Point();
point.x = 10 ;
point.y = 10 ;
point.print(); // 输出点 (10,10)
// testPoint(point); // 实际参数, 是引用类型。
// point.print(); // 输出点(11 ,10)
testPoint1(point); // 实际参数,是引用类型。
point.print(); // 输出点(10,10)
System.out.println("---------------特殊的字符串引用类型-----------------
-");
String str = "你好世界"; // 创建对象, 赋值给引用。
testStr(str);
System.out.println("最后的str:" + str); //
System.out.println("----------值传递----------");
int i = 3;
int k = 5;
testInt(i,k);
System.out.println("最后的i,k:" + i +"," +k);
}
public static void testPoint(Point p){ // 参数是引用类型
p.right(); // 右边移动一个位置。
}
public static void testPoint1(Point p){
p = new Point(); // 给引用赋值了一个新对象。 实际参数传入的引用对应的对象不会
被修改。
p.right();
}
public static void testStr(String str){
System.out.println("str:" + str); // 使用。
// 每个字符串都是一个新对象。
str = "中国"; // 把字符串对象"中国"的引用地址 ,赋值给str这个应用了。
System.out.println("str:" + str);
}
public static void testInt(int i , int k){ // 基本数据类型, 值传递, 把
i,k的值取出来传入这个方法中使用。
i = 10*i;
k = 10*k;
System.out.println("i=" +i +"," + "k=" +k);
}
}
23.== 和equals的区别
== , 基本数据类型,判断数据值是否相等
== , 引用类型,判断引用类指向的地址是否相等,即是否为同一个对象。
equals是父类Object中提供的的一个equals方法,用于制定两个对象是否相等的比较规则。
Object中的equals默认是比较两个对象的地址是否相同。子类重写父类的equals方法,自定义比较规则:
比如: 判断两个点是否相同,可以判断两个点的x位置和y位置同时相等,则两个点相同。
如果两个对象比较equals为true ,那么这依然不能说明是否为同一个对象。
重写类的equals方法: 使用idea的自动生成
// 定义类型: 成员变量,成员方法。
// *** java中的所有类都有父类。 如果你定义的类型没有写extends那个父类,那么这个类的父类
就是Object 。 一般省略extends Object.
// 继承父类的属性 和 方法 ,如果父类的方法不满足子类的需求, 就重写父类的方法。
public class Point extends Object {
int x;
int y;
public void up(){
this.y ++ ;
}
public void down(){
this.y --;
}
public void left(){
this.x --;
}
public void right(){
this.x ++;
}
public void print(){
System.out.println("(" + this.x + "," + this.y + ")");
}
@Override
public boolean equals(Object o) {
// 判断是否为自己
if (this == o)
return true;
// 和空比较,直接就是false.
// getClass() != o.getClass() .如果是不同的类型进行比较,直接就是false.
// ** getClass() ,获取到的是对象的类型。
if (o == null || this.getClass() != o.getClass())
return false;
// 因为o不是null, 又是同种类型,所以就强制数据类型。
Point point = (Point) o;
// x ,y 同时相等,返回true, 否则返回false.
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
参考代码
// == : 用于判断基本数据类型的数据值是否相等 , 用于判断引用类型的地址是否相等。
// ** 如果引用地址相等,说明一定是指向的同一个对象,否则,说明是指向不
同的对象。
// equals: 类中的方法 , 用于判断对象的内容是否相等。
public class EqualsDemo {
public static void main(String[] args) {
int a = 10 ;
int b = 10 ;
System.out.println(a == b); // true.
Point p1 = new Point();
p1.x = 10 ;
p1.y = 10 ;
Point p2 = new Point();
p2.x = 10 ;
p2.y = 10 ;
System.out.println("p1:" + p1);
System.out.println("p2:" + p2);
System.out.println(p1 == p2); // 用于==判断引用类型地址是否相等。 false
Student stu1 = new Student("1001" , "小红" ,"java2202");
Student stu2 = new Student("1001" , "小红" ,"java2202");
System.out.println("stu1:" + stu1);
System.out.println("sut2:" +stu2);
System.out.println(stu1 == stu2);
Student stu3 = stu1;
System.out.println(stu3 == stu1); // true. 如果两个引用使用==判断,结果
为true,说明这是引用的同一个对象。
// *** 如果规定 Point类型,如果x ,y 和另一个对象的x,y相等,我们就认为这两个
点是同一个点。
// 如果规定Student类型,code, name, className各自相等,就认为是同一个
学生。
// ** 如果没有重写equals方法
// System.out.println(p1.equals(p2)); // false . ** Object中定义的
equals方法,就是用==判断两个对象的地址是否相等。
// ** 如果重写了equals方法
System.out.println(p1.equals(p2)); // true , 重写了equals方法,制定了比
较规则: x ,y同时相等,则两个点相等。
Point p3 = new Point();
p3.x = 10 ; p3.y = 5;
System.out.println(p1.equals(p3)); // false 。 因为x和y没有同时相等。所以
equals方法,返回false.
System.out.println(p1.equals(null)); // false. 空不能和任何对象相等。
System.out.println(p1.equals(stu3)); // false. 不同类型,不能比。
System.out.println(p1.equals(p1)); // true , 自己和自己相等。
24.面向对象编程&面向过程编程
Object Oriented Programming 简称OOP: c++ ,java , c#
Procedure Oriented Programming 简称POP: c语言 面向过程,侧重于分析问题的步骤和具体细节,亲力亲为地去完成每一步都是基于上一步去完成 面向对象,侧重于创建解决问题的实体,通过实体互相配合去完成每个实体解决问题的细节实质也 是面向过程的
25.面向对象三大特性:
25.1封装
通过private关键字对成员变量进行修饰,提供公开的getter和setter方法用于简单地对成员变量进行取值和赋值。
可以隐藏具体的细节,实现代码安全
在Idea中,自动生成getter/setter的快捷键为:右键--generate或alt+insert -->getter and setter
选择要生成的成员变量
25.2继承
A类通过extends关键字,继承B类,Java是中是单继承,一个子类只能有一个父类,一个父类可以有多个子类
继承能够提高代码的重用性
Java是中是单继承,一个子类只能有一个父类,一个父类可以有多个子类
25.3多态
对象是多种状态的。
父类的引用指向子类的对象
Father s=new Son();
26.静态代码块
静态代码块
非静态代码块
程序运行
面向对象面试题
面向对象的特点?
封装
继承
多态
你怎么理解面向对象?
面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。 面向对象是相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。
面向对象和面向过程的区别?
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
其实就是两句话,面向对象就是高度实物抽象化、面向过程就是自顶向下的编程。
接口和抽象类的区别是什么 ?
1.抽象类要被子类继承,接口要被类实现。
2.接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
3.接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
4.接口是设计的结果,抽象类是重构的结果。
5.抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高。
6.抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。
7.抽象类主要用来抽象类别,接口主要用来抽象功能。
成员变量与局部变量的区别有那些 ?
成员变量:包括实例变量和类变量,用static修饰的是类变量,不用static修饰的是实力变量,所有类的成员变量可以通过this来引用。
局部变量:包括形参,方法局部变量,代码块局部变量,存在于方法的参数列表和方法定义中以及代码块中。
创建一个对象用什么运算符?对象实体与对象引用有何不同?
创建一个对象使用new运算符。new创建的对象实例在堆内存中,而对象引用指向对象实例,对象引用存放在栈内存中。
一个对象引用可以指向0个或1个对象实体,而一个对象可以有多个引用来指向它。
什么是方法的返回值?返回值在类的方法里的作用是什么?
方法的返回值是某个方法体中的代码执行后产生的结果。
返回值的作用是接受结果,使得它可以用于其它操作。
一个类的构造方法的作用是什么?若一个类没有声明构造方法,该程序能正确执行吗 ?为什么?
构造方法主要作用是完成对类对象的初始化工作。
如果一个类没有声明构造方法,也可以执行!因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。如果我们自己添加了类的构造方法(无论是否有参),Java 就不会再添加默认的无参数的构造方法了,这时候,就不能直接 new 一个对象而不传递参数了,所以我们一直在不知不觉地使用构造方法,这也是为什么我们在创建对象的时候后面要加一个括号(因为要调用无参的构造方法)。如果我们重载了有参的构造方法,记得都要把无参的构造方法也写出来(无论是否用到),因为这可以帮助我们在创建对象的时候少踩坑。
构造方法有哪些特性 ?
1.名字与类名相同。
2.没有返回值,但不能用 void 声明构造函数。
3.生成类的对象时自动执行,无需调用。
对象的相等与指向他们的引用相等,两者有什么不同?
对象的相等,比的是内存中存放的内容是否相等。而引用相等,比较的是他们指向的内存地址是否相等。
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
为了实现对象的实例化.
子类中所有的构造函数都会默认调用父类中的无参构造函数, 因为每一个子类构造函数内第一行都有隐式的 super();
若父类中没有无参构造函数, 那么子类的构造函数内必须通过 super 语句指定要调用的父类中的构造函数.
若子类构造函数中用 this 来指定调用子类自己的构造函数, 那么被调用的构造函数也一样会调用父类中的构造函数.
构造块、静态块、构造方法中的方法执行顺序是怎样的?
类加载时,执行静态代码块和初始化静态变量。执行顺序跟代码顺序一致。
2、类实例化时,初始化变量、执行代码块、执行构造。其中初始化变量和代码块的顺序跟代码顺序一致。执行构造是在最后。
3、实例化子类时,会先调用父类的构造方法。调用构造方法之前,会先执行该类的代码块。
4、类只会加载一次。
5、静态变量或静态方法中如果调用构造,可以把构造当做一个普通方法来看。但会先执行一遍代码块。
普通类和抽象类有哪些区别?
1、抽象类的存在时为了被继承,不能实例化,而普通类存在是为了实例化一个对象
2、抽象类的子类必须重写抽象类中的抽象方法,而普通类可以选择重写父类的方法,也可以直接调用父类的方法
3、抽象类必须用abstract来修饰,普通类则不用
4、普通类和抽象类都可以含有普通成员属性和普通方法
5、普通类和抽象类都可以继承别的类或者被别的类继承
6、普通类和抽象类的属性和方法都可以通过子类对象来调用
抽象类能使用 final 修饰吗?
不能
补充:关于 final 关键字的一些总结
final关键字修饰的类不可以被继承
对于一些类,设置了final修饰,可以避免滥用继承造成的危害
final关键字修饰的方法可以不被重写 防止子类在定义自己的方法的时候,不经意被重写。
final修饰成员变量,意为不可以改变。 该成员变量需要在初始化的时候赋值,对象一旦创建,即该值不可以改变。