面向过程
定义:当我们在解决一个问题时,会按照预先设定的想法和步骤,一步一步去实现,在这里每一步具体的实现中都需要我们自己去亲自实现和操作。
我们的角色是: 执行者
特点:费时间、费精力、结果也不一定完美
面向对象
定义:当我们在解决一个问题时,可以去找具有相对应的功能的事物帮着我们去解决问题,至于这个事物如何工作,与我们无关,我们只看结果,不关心解决流程。
我们的角色是: 指挥者
特点:节省时间,节省精力,结果相对完美
面向对象和面向过程差异
什么是对象
面向对象编程语言主要是使用对象们来进行相关的编程。对象,万事万物中存在的每一个实例,一个电脑、一个手机、一个人、抖音里的一个短视频、支付宝里的一个交易记录、淘宝里的订单。
如何去描述一个对象的内容?
什么是类
类是那些具有相同属性特征和行为的对象们的统称。对象就是该类描述下具体存在的一个事物。
封装=包装
常见的封装体现
private关键字,属于权限关键字 public protected 默认不写 private
private可以作用在对象属性和行为上,外界在创建对象后,则不能访问被private修饰的内容
public class Sample {
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("小强");
p1.setAge(10);
p1.speak();
}
}
class Person {
private String name;
private int age;
public void speak() {
System.out.println("我是" + name + ",我今年" + age + "岁");
}
public void setName(String name) {
if (name.equals("旺财")) {
this.name = "哈士奇";
} else {
this.name = name;
}
}
public void setAge(int age) {
if (age < 0) {
this.age = 0;
} else {
this.age = age;
}
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
局部变量和成员变量有什么区别?
成员变量随着对象的创建而创建,随着对象的消亡而消失
局部变量随着函数的进栈而创建,随着函数的出栈而消失
成员变量在堆内存中对象所属空间里
局部变量在栈内存中函数所属空间里
成员函数在类中,函数外定义
局部变量在函数中定义
成员变量有默认初始化
局部变量必须初始化之后再调用
什么是构造函数:构造函数主要是在创建对象的时候执行的函数,在该函数中也可对成员变量进行一些操作。
构造函数的格式
权限修饰符 类名(参数列表) {
构造函数的代码块
}
构造函数需要注意的问题
public class Sample {
public static void main(String[] args) {
Person p = new Person();//new 构造函数;
p.setName("旺财");
p.setAge(10);
p.speak();
Person p2 = new Person();//new 构造函数;
p2.setName("小强");
p2.setAge(20);
p2.speak();
Person p3 = new Person("如花",40);
p3.speak();
p3.test();
//p3.part();
Car car1 = new Car();
Car car2 = new Car(4);
Car car3 = new Car(4,"红色");
Car car4 = new Car(8,"武士黑",20);
car1.run();
car2.run();
car3.run();
car4.run();
}
}
class Car {
private int wheel = 4;
private String color;
private int weight;
public Car(){
}
public Car(int wheel) {
this(wheel,null,0);
}
public Car(int wheel,String color) {
this(wheel,color,0);
}
public Car(int wheel,String color,int weight) {
this.wheel = wheel;
this.color = color;
this.weight = weight;
//this();//递归构造器调用
}
public void run() {
System.out.println(wheel + ":" + color + ":" + weight);
}
//这个不是重载 Car(int)已经存在了(wheel)
/*
public Car(int weight) {
}
*/
}
class Person {
private String name;
private int age;
//这就是隐藏的构造函数
public Person() {
System.out.println("一个Person创建出来了!");
part();
part();
part();
part();
}
//语意
private void part() {
System.out.println("100行代码");
}
public Person(String name,int age) {
System.out.println("一个Person创建出来了!");
this.name = name;
this.age = age;
}
public void test() {
Person();//实际上这一段代码并不表示调用无参构造函数
//而表示去调用名字为Person的成员函数!
}
public void Person() {
System.out.println("没想到吧!");
}
public void speak() {
System.out.println(name + ":" + age);
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
关于成员变量初始化的问题
成员变量的初始化经历了三个步骤:默认初始化(大家默认都是0值),显式初始化(大家的值都一样),针对性初始化(大家的值可选)。
public class Sample{
public static void main(String[] args) {
Stack stack = new Stack();
stack.pop();
System.out.println(stack);
for(int i=1; i<=10;i++){
stack.push(i);
}
System.out.println(stack);
stack.pop();
System.out.println(stack);
System.out.println(stack.peek());
stack.clear();
System.out.println(stack);
System.out.println(stack.peek());
}
}
class Stack{
private int[] data;
private int top = -1;
private static int capacity = 10;
public Stack() {
this(capacity);
}
public Stack(int capacity) {
data = new int[capacity];
}
public void push(int e){
if(size() == data.length) {
reSize(data.length);
}
data[++top] = e;
}
public void reSize(int length){
int[] arr = new int[length * 2];
for(int i=0; i<= top; i++) {
arr[i] = data[i];
}
data = arr;
}
public int pop(){
if(isEmpty()){
System.out.println(">>>栈为空,弹不出元素了!");
return -1;
}
int e = data[top];
top--;
if(data.length > capacity && size() == data.length / 4) {
reSize(data.length / 2);
}
return e;
}
public void clear(){
top = -1;
data = new int[10];
}
public int peek(){
if(isEmpty()){
System.out.println(">>>栈为空,无栈顶元素!");
return -1;
}
return data[top];
}
public boolean isEmpty(){
return top == -1;
}
public int size() {
return top + 1;
}
public String toString() {
if(isEmpty()){
return "[]";
}
String s = "[";
for(int i=0; i <= top; i++) {
if(i == top){
s += data[i] + "]";
}else{
s += data[i] + ", ";
}
}
return s;
}
}
public class Sample02{
public static void main(String[] args) {
Player p1 = new Player("老王",100);
Player p2 = new Player("老李",100);
p1.shootEnemy(p2);
Gun gun = new Gun();
p1.holdGun(gun);
p1.shootEnemy(p2);
Clip clip = new Clip();
for (int i = 1; i <= 30; i++) {
clip.pushBullet(new Bullet());
}
p1.loadClip(clip);
for (int i = 1; i <= 30; i++) {
p1.shootEnemy(p2);
}
p1.shootEnemy(p2);
}
}
//人
class Player{
//人物姓名
private String name;
//人物血值
private int blood;
//人物的枪
private Gun gun;
public Player() {
}
//人物初始化
public Player(String name,int blood){
this(name,blood,null);
}
//人物初始化
public Player(String name,int blood,Gun gun){
this.name = name;
this.blood = blood;
this.gun = gun;
}
//设置枪
public void holdGun(Gun gun) {
this.gun = gun;
}
//攻击敌人
public void shootEnemy(Player enemy) {
if(gun == null) {
System.out.println(">>>玩家信息:没有枪,开P");
}else{
System.out.printf(">>>玩家信息:%s向%s开了一枪\n",name,enemy.name);
gun.shootEnemy(enemy);
}
}
//上弹夹
public void loadClip(Clip clip) {
if(gun == null){
System.out.println(">>>玩家信息:没抢,装不了弹夹");
}else{
gun.loadClip(clip);
}
}
//玩家受到攻击
public void damage(int hurt) {
//血量为0,攻击不了
if(blood == 0) {
System.out.println(">>>玩家信息:" + name + "已经成盒,请勿鞭尸");
}else{
//否则扣血
blood -= hurt;
if(blood > 0){
System.out.println(">>>玩家信息:" + name + "掉血" + hurt + ",剩余" +blood);
}else{
//扣血小于0后,重新赋血值,输出信息
blood = 0;
System.out.println(">>>玩家信息:" + name + "已经成盒");
}
}
}
}
//枪
class Gun{
private Clip clip;
public Gun() {
this(null);
}
//初始化弹夹
public Gun(Clip clip) {
this.clip = clip;
}
//上弹夹
public void loadClip(Clip clip) {
this.clip = clip;
}
//攻击敌人
public void shootEnemy(Player enemy) {
//判断枪是否有弹夹
if(clip == null) {
System.out.println(">>>枪信息:没有弹夹,开了个空枪");
return;
}
//弹夹弹出一颗子弹
Bullet bullet = clip.popBullet();
//弹出子弹为空,则弹夹没有子弹
if(bullet == null) {
System.out.println(">>>枪信息:弹夹没子弹 开了个空枪");
return;
}else{
bullet.hitEnemy(enemy);
}
}
}
//弹夹
class Clip{
//弹夹的容量
private int capacity = 30;
//弹夹剩余的子弹
private int surplus = 0;
//弹夹的子弹容器
private Bullet[] magazine;
public Clip(){
this(30);
}
//初始化子弹容器和弹夹的容量
public Clip(int capacity) {
this.capacity = capacity;
magazine = new Bullet[capacity];
}
//装入一颗子弹
public void pushBullet(Bullet bullet) {
//判断弹夹中子弹是否已经装满
if(surplus == capacity) {
System.out.println(">>>弹夹信息:弹夹已满,无法装入子弹");
return;
}else{
magazine[surplus++] = bullet;
showClip();
}
}
//弹出一颗子弹
public Bullet popBullet() {
//判断弹夹中是否有子弹
if(surplus == 0){
System.out.println(">>>弹夹信息:弹夹为空,无法弹出子弹!");
return null;
}else {
//从子弹容器中取出一颗子弹
Bullet bullet = magazine[surplus-1];
//剩余子弹--
surplus--;
showClip();
return bullet;
}
}
public void showClip() {
System.out.printf(">>>弹夹信息:%d/%d\n",surplus,capacity);
}
}
//子弹
class Bullet{
//子弹的伤害
private int hurt;
public Bullet() {
}
//为子弹赋特殊伤害
public Bullet(int hurt) {
this.hurt = hurt;
}
//子弹伤害敌人
public void hitEnemy(Player enemy) {
enemy.damage(hurt);
}
}
静态关键字
主要用于:修饰成员变量(对象的特有属性)和成员函数,变为静态变量和静态函数
静态变量的特点:同类下多个对象之间的共有属性
静态变量的定义时机:在同一类下,多个对象之间有相同的属性和值,那么就可以将该属性和值从成员变量变为静态变量,目的就是为了节省空间。
静态函数的定义时机:当一个成员函数不访问成员时,即可定义为静态函数!
1.当通过对象去调用一个属性时,先找成员,再找静态,最后找父类
2.如果从成员函数中去调用一个属性时,先找局部,再找成员,再找静态,最后找父类
好处:
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
Chinese c1 = new Chinese();
Chinese c2 = new Chinese();
Chinese c3 = new Chinese();
System.out.println(c1.country);
System.out.println(Chinese.country);
c1.show();
Chinese.show();
/*
Chinese.test();
*/
}
}
class Chinese {
String name;
int age;
static String country;
//静态代码块
static {
country = "China";
System.out.println("init....");
}
Chinese() {
System.out.println("Chinese....");
}
public void test(){
int num = 10;
System.out.println(num + name + country);
}
public static void show() {
/*
//无法从静态上下文中引用非静态 方法 test()
test();
//无法从静态上下文中引用非静态 变量 name
System.out.println("show...." + name);
*/
}
}
存储位置
生命周期
所属不同
调用方式不同
成员变量在外界必须通过创建对象来调用,内部的话成员函数可以直接调用成员变量,但是静态函数不能直接调用成员变量,如果非要在静态函数中调用成员的话,只能创建对象,通过对象来调用
静态变量在外界可以通过对象调用,也可以通过类来调用,内部的话静态函数/成员函数可以调用静态变量
public class Demo {
public static void main(String[] args) {
//StackOverFlowError 栈内存溢出
A a = new A();
System.out.println(a == a.a);
System.out.println(a.a == a.a.a);
}
}
class A {
//OutOfMemoryError 堆内存溢出
int[][] arr = new int[1024][1024];
A a = new A();
}
public class Demo {
public static void main(String[] args) {
A a = new A();
System.out.println(a == a.a);
System.out.println(a.a == a.a.a);
}
}
class A {
static A a = new A();
}
public class Demo106{
public static void main(String[] args){
Rectangle r1 = new Rectangle();
System.out.println(r1.getArea());
System.out.println(r1.getPermeter());
Rectangle r2 = new Rectangle(4.0,5.0);
System.out.println(r2.getArea());
System.out.println(r2.getPermeter());
}
}
class Rectangle{
private double width = 1;
private double height = 1;
public Rectangle(){
}
public Rectangle(double width,double height){
this.width = width;
this.height = height;
}
public double getArea(){
return width * height;
}
public double getPermeter(){
return 2 * (width + height);
}
}
public class Demo107 {
public static void main(String[] args) {
StopWatch sw = new StopWatch();
sw.start();
for(int i = 0; i<1000000; i++){
}
sw.stop();
System.out.println(sw.getElapsedTime());
}
}
class StopWatch {
private long startTime;
private long endTime;
public StopWatch(){
startTime = System.currentTimeMillis();
}
public void start() {
startTime = System.currentTimeMillis();
}
public void stop() {
endTime = System.currentTimeMillis();
}
public long getElapsedTime() {
return endTime - startTime;
}
}
public class Demo108 {
public static void main(String[] args) {
Fan f1 = new Fan();
System.out.println(f1);
f1.setOn(true);
f1.setSpeed(Fan.FAST);
f1.setColor("red");
f1.setRadius(10);
System.out.println(f1);
}
}
class Fan {
public static final int SLOW = 1;
public static final int MEDIUM = 2;
public static final int FAST = 3;
private int speed = SLOW;
private boolean on = false;
private double radius = 5;
private String color = "blue";
public Fan(){
}
public void setSpeed(int speed){
this.speed = speed;
}
public int getSpeed(){
return this.speed;
}
public void setOn(boolean on){
this.on = on;
}
public boolean isOn(){
return this.on;
}
public void setRadius(int radius){
this.radius = radius;
}
public double getRadius(){
return this.radius;
}
public void setColor(String color){
this.color = color;
}
public String getColor(){
return this.color;
}
public String toString(){
if(on){
return "The Fan speed is "+getSpeed()+" color is "+getColor()+" radius is "+getRadius();
}else{
return "Fan is off and the color is "+getColor()+" radius is "+getRadius();
}
}
}
public class Demo109 {
public static void main(String[] args) {
RegularPolygon r1 = new RegularPolygon();
System.out.println(r1.getArea());
System.out.println(r1.getPerimeter());
RegularPolygon r2 = new RegularPolygon(5,6,5,6);
System.out.println(r2.getArea());
System.out.println(r2.getPerimeter());
}
}
class RegularPolygon {
private int n = 3;
private double side = 1;
private double x = 0;
private double y = 0;
public RegularPolygon(){
}
public RegularPolygon(int n,double side) {
this.n = n;
this.side = side;
}
public RegularPolygon(int n,double side,double x,double y) {
this.n = n;
this.side = side;
this.x = x;
this.y = y;
}
public double getPerimeter(){
return n * side;
}
public double getArea() {
return n * Math.pow(side,2) / (4 * Math.tan(Math.PI / n));
}
public void setN(int n){
this.n = n;
}
public int getN(){
return this.n;
}
public void setSide(double side){
this.side = side;
}
public double getSide(){
return this.side;
}
public void setX(double x){
this.x = x;
}
public double getX(){
return this.x;
}
public void setY(double y){
this.y = y;
}
public double getY(){
return this.y;
}
}
public class Demo110{
public static void main(String[] args) {
QuadraticEquation q = new QuadraticEquation(3,4,1);
System.out.println(q.getRoot1());
System.out.println(q.getRoot2());
}
}
class QuadraticEquation {
private double a;
private double b;
private double c;
public QuadraticEquation(double a,double b,double c){
this.a = a;
this.b = b;
this.c = c;
}
public double getDiscriminant(){
double result = Math.pow(b,2) - 4 * a * c;
if(result < 0){
return 0;
}
return result;
}
public double getRoot1() {
return (-b + Math.sqrt(getDiscriminant())) / 2 * a;
}
public double getRoot2() {
return (-b - Math.sqrt(getDiscriminant())) / 2 * a;
}
public double getA(){
return this.a;
}
public void setA(double a){
this.a = a;
}
public double getB(){
return this.b;
}
public void setB(double b){
this.b = b;
}
public double getC(){
return this.c;
}
public void setC(double c){
this.c = c;
}
}
import java.util.*;
public class Demo111{
public static void main(String[] args){
System.out.print("Enter the number of rows and columns in the array: ");
Scanner input = new Scanner(System.in);
int row = input.nextInt();
int column = input.nextInt();
double [][] array = new double[row][column];
System.out.println("Enter the array:");
for(int i =0; i<array.length;i++){
for(int j=0;j<array[i].length;j++){
array[i][j] = input.nextDouble();
}
}
Location l = new Location();
Location a = l.locateLargest(array);
System.out.println("The location of the largest element is "+a.getMax()+" at ("+a.getRow()+", "+a.getColume()+")");
}
}
class Location{
private int row;
private int column;
private double maxValue;
public int getRow(){
return this.row;
}
public void setRow(int row){
this.row = row;
}
public double getMax(){
return this.maxValue;
}
public void setMax(double maxValue){
this.maxValue = maxValue;
}
public int getColume(){
return this.column;
}
public void setColume(int column){
this.column = column;
}
public static Location locateLargest(double[][] a){
int row = 0,column = 0;
double maxValue = 0;
for(int i = 0; i<a.length; i++){
for(int j = 0;j<a[i].length;j++){
maxValue = a[i][j] > maxValue ? a[i][j] : maxValue;
row = i;
column = j;
}
}
Location l = new Location();
l.row = row;
l.column =column;
l.maxValue = maxValue;
return l;
}
}
public class Demo112 {
public static void main(String[] args) {
Time t1 = new Time();
System.out.println(t1.toString());
Time t2 = new Time(120233131L);
System.out.println(t2.toString());
Time t3 = new Time(12,30,56);
System.out.println(t3.toString());
t3.setTime(System.currentTimeMillis());
System.out.println(t3.toString());
}
}
class Time {
private long hour;
private long minute;
private long second;
public Time() {
this(System.currentTimeMillis());
}
public Time(long millis) {
hour = cacuHour(millis);
minute = cacuMinute(millis);
second = cacuSecond(millis);
}
public long getHour(){
return hour;
}
public long getMinute(){
return minute;
}
public long getSecond(){
return second;
}
public void setTime(long elapseTime) {
hour = cacuHour(elapseTime);
minute = cacuMinute(elapseTime);
second = cacuSecond(elapseTime);
}
public String toString(){
return hour+":"+minute+":"+second;
}
public Time(long hour,long minute,long second) {
this.hour = hour;
this.minute = minute;
this.second = second;
}
private long cacuHour(long millis) {
return millis / 1000 / 60 / 60 % 24 + 8;
}
private long cacuMinute(long millis) {
return millis / 1000 / 60 % 60;
}
private long cacuSecond(long millis) {
return millis / 1000 % 60;
}
}
public class Demo113 {
public static void main(String[] args) {
MyInteger m1 = new MyInteger(3);
System.out.println(m1.isEven());
System.out.println(m1.isOdd());
System.out.println(m1.isPrime());
MyInteger m2 = new MyInteger(4);
MyInteger m3 = new MyInteger(13);
System.out.println(MyInteger.isEven(m2));
System.out.println(MyInteger.isPrime(m3));
System.out.println(m2.equals(m3));
System.out.println(MyInteger.parseInt("1234") + 1);
}
}
class MyInteger {
private int value;
public MyInteger(int value) {
this.value = value;
}
public int get() {
return value;
}
public boolean isEven() {
return value % 2 == 0;
}
public boolean isOdd() {
return value % 2 == 1;
}
public boolean isPrime() {
for (int i = 2; i <= value / 2; i++) {
if (value % i == 0) {
return false;
}
}
return true;
}
public static boolean isEven(MyInteger integer) {
return integer.get() % 2 == 0;
}
public static boolean isOdd(MyInteger integer) {
return integer.get() % 2 == 1;
}
public static boolean isPrime(MyInteger integer) {
for (int i = 2; i <= integer.get() / 2; i++) {
if (integer.get() % i == 0) {
return false;
}
}
return true;
}
public boolean equals(int num) {
return value == num;
}
public boolean equals(MyInteger integer) {
return value == integer.get();
}
public static int parseInt(String str) {
int result = 0;
for (int i = 0; i < str.length(); i++) {
int num = str.charAt(i) - '0';
result = num + result * 10;
}
return result;
}
}
public class Demo114 {
public static void main(String[] args) {
MyPoint p1 = new MyPoint();
MyPoint p2 = new MyPoint(10,30.5);
System.out.println(p1.distance(p2));
System.out.println(p2.distance(11,11));
}
}
class MyPoint {
private double x = 0;
private double y = 0;
public MyPoint(){
}
public MyPoint(double x, double y){
this.x = x;
this.y = y;
}
public double distance(MyPoint point){
return Math.hypot(point.getX() - x,point.getY() - y);
}
public double distance(double x, double y){
return Math.hypot(x - this.x,y - this.y);
}
public double getX(){
return x;
}
public double getY(){
return y;
}
}
public class Demo115 {
public static void main(String[] args) {
Queue q1 = new Queue();
System.out.println(q1);
//遍历添加10个元素,队列进行扩容
for(int i=1; i <= 10;i++){
q1.enqueue(i);
}
System.out.println(q1);
//队列出6个元素,此时有效元素的个数小于队列的1/4大小,进行缩容
for(int i=1;i<=6;i++){
System.out.println(q1.dequeue());
System.out.println(q1);
}
System.out.println("当前队列的有效元素个数为:"+q1.getSize());
}
}
class Queue {
//队列容器用于存储元素,element.length == size 表示队列已满
private int[] element;
//队列中有效元素的个数
private int size;
//队列容器的默认值
private int capacity = 8;
//默认队列初始化为8
public Queue(){
element =new int[capacity];
size = 0;
}
//向队列添加一个元素
public void enqueue(int v) {
//先判断该队列是否已经满了,满了的话对其进行扩容1倍
if(size == element.length){
//扩容1倍
resize(element.length * 2);
}
//扩容还是没用扩容,都要将元素添加进队列,size要自增
element[size++] = v;
}
//删除并返回队列的第一个元素
public int dequeue() {
int result = element[0];
//当删除元素后队列的有效长度为当前队列的四分之一时,对队列进行缩容,并且默认缩容到最小为8
if(size <= element.length / 4 && element.length > capacity){
//对队列缩容二分之一,保证在入队时进行不必要的扩容,留有容量进行增加元素
resize(element.length / 2);
}
for(int i=1; i<size;i++){
element[i-1] = element[i];
}
//队列出队一个元素size要减1
size--;
return result;
}
//扩容和缩容函数
private void resize(int len) {
int[] newArray = new int[len];
//遍历原数组进行复制元素到新的数组
for(int i=0; i<size;i++) {
newArray[i] = element[i];
}
element = newArray;
}
//打印队列中的元素
public String toString(){
String str = "[";
//判断队列是否为空
if(isEmpty()){
return str += "]";
}else{
//遍历数组打印内容,并判断是否为最后一个元素,是的话就加],否则就加,
for(int i=0; i<size; i++){
if(i == size - 1){
str += element[i]+"]";
}else{
str += element[i]+",";
}
}
}
return str;
}
//判断队列是否为空
public boolean isEmpty() {
return size == 0;
}
//返回队列的大小
public int getSize() {
return size;
}
}
public class Demo117 {
public static void main(String[] args) {
char[] ch = {
'a','b','c'};
MyString s1 = new MyString(ch);
MyString s2 = new MyString("abg");
System.out.println(s1.compareTo(s2));
MyString s3 = new MyString("PPviuiNN");
s3.toLowerCase().show();
s1.toUpperCase().show();
System.out.println(s2.compareToIgnoreCase(s3));
s3.concat(s1).show();
System.out.println(s1.contains(s2));
MyString s4 = new MyString("xxx.mp4");
System.out.println(s4.endsWith(new MyString(".mp4")));
System.out.println(s4.equals(new MyString("xxx.mp")));
System.out.println(s4.equalsIgnoreCase(new MyString("xxx.MP4")));
System.out.println(s4.indexOf('m'));
System.out.println(s4.lastIndexOf('x'));
System.out.println(s3.indexOf(new MyString("iui")));
System.out.println(s3.lastIndexOf(new MyString("iui")));
s3.replace('P','a').show();
System.out.println(s3.startWith(new MyString("PP")));
s3.substring(2,5).show();
}
}
class MyString {
//定义一个字符数组来存放字符串
private char[] data;
public MyString(char[] chars) {
//将外部的字符数组,传递给内部的字符数组
data = new char[chars.length];
for(int i=0; i<chars.length; i++) {
data[i] = chars[i];
}
}
//将外部字符串传递给内部的data
public MyString(String s) {
data = new char[s.length()];
for(int i=0; i<s.length();i++){
data[i] = s.charAt(i);
}
}
//截取字符串
public MyString substring(int beginIndex,int endIndex){
char[] ch = new char[endIndex-beginIndex];
int index = 0;
for(int i=beginIndex;i<endIndex;i++){
ch[index++] = data[i];
}
return new MyString(ch);
}
//判断是否以字符开头
public boolean startWith(MyString s){
//定义两个指针,分别判断两个字符串的首字母是否相等,相等就继续判断
//判断到有一个字符串溢出为止,有溢出就返回true,否则就为false
int i = 0;
int j = 0;
while(true){
if(charAt(i) == s.charAt(j)){
i++;
j++;
if(j >= s.length()){
return true;
}
}else{
return false;
}
}
}
//替换字符串中的字符
public MyString replace(char oldChar,char newChar){
char[] chars = new char[length()];
for(int i=0;i<length();i++){
if(charAt(i) == oldChar){
chars[i] = newChar;
}else{
chars[i] = data[i];
}
}
return new MyString(chars);
}
//返回第一个字符相等的角标
public int indexOf(char c){
for(int i=0;i<length();i++){
if(charAt(i) == c){
return i;
}
}
return -1;
}
//返回最后一个字符相等的角标
public int lastIndexOf(char c){
for(int i=length()-1;i>=0;i--){
if(charAt(i) == c){
return i;
}
}
return -1;
}
//返回字符串的第一个角标
public int indexOf(MyString s){
//将两个数组变为局部变量,方便比较
char[] ch1 = data;
char[] ch2 = s.data;
//循环数组,每次比较两个字符串的首字母是否相等,相等的就遍历第二个字符串
//并与接下去的第一个字符串的字符比较,只要有一个不相等就返回-1,否则就返回i对应的角标
for(int i=0;i<length() - ch2.length;i++){
if(ch1[i] == ch2[0]){
int j = i+1;
for(int l=1;l<ch2.length;l++,j++){
if(ch1[j] != ch2[l]){
return -1;
}
}
return i;
}
}
return -1;
}
//返回字符串相等的最后的一个角标
public int lastIndexOf(MyString s){
//将两个数组变为局部变量,方便比较
char[] ch1 = data;
char[] ch2 = s.data;
//循环数组,每次比较两个字符串的首字母是否相等,相等的就遍历第二个字符串
//并与接下去的第一个字符串的字符比较,只要有一个不相等就返回-1,否则就返回i对应的角标
for(int i = ch1.length-1;i>=ch2.length-1;i--){
if(ch1[i] == ch2[ch2.length-1]){
int j = i -1;
for(int l=ch2.length-2;l>=0;l--,j--){
if(ch1[j] != ch2[l]){
return -1;
}
}
return j+1;
}
}
return -1;
}
//判断两个字符串是否相等忽略大小写
public boolean equalsIgnoreCase(MyString s){
return compareToIgnoreCase(s) == 0;
}
//判断两个字符串是否相等
public boolean equals(MyString s){
return compareTo(s) == 0;
}
//判断字符串是否以xx结尾
public boolean endsWith(MyString s) {
//都从字符串的最后一个字符开始遍历
//判断两个字符是否相等,不相等返回false,直到有一个字符遍历完了后就返回true
int i = length() - 1;
int l = s.length() - 1;
while(true){
if(charAt(i) == s.charAt(l)){
i--;
l--;
if(l<0){
return true;
}
}else{
return false;
}
}
}
//判断一个字符串是否包含另一个字符串
public boolean contains(MyString s){
//将两个数组变为局部变量,方便比较
char[] ch1 = data;
char[] ch2 = s.data;
//循环数组,每次比较两个字符串的首字母是否相等,相等的就遍历第二个字符串
//并与接下去的第一个字符串的字符比较,只要有一个不相等就返回false,否则就返回true
for(int i=0;i<length() - ch2.length;i++){
if(ch1[i] == ch2[0]){
int j = i+1;
for(int l=1;l<ch2.length;l++,j++){
if(ch1[j] != ch2[l]){
return false;
}
}
return true;
}
}
return false;
}
//连接两个字符串
public MyString concat(MyString s) {
//创建一个行的数组,长度为两个字符串的总和
//分别遍历两个数组,将其内容复制到新的数组中返回即可
char[] chars = new char[length() + s.length()];
int index =0;
for(int i=0;i<length();i++){
chars[index++] = data[i];
}
for(int i=0; i<s.length();i++){
chars[index++] = s.charAt(i);
}
return new MyString(chars);
}
//比较两个字符串的大小,忽略大小写
public int compareToIgnoreCase(MyString s) {
//将两个字符串都转为小写的字符串,在比较大小
MyString s1 = toLowerCase();
MyString s2 = s.toLowerCase();
return s1.compareTo(s2);
}
//将字符串中大写的字母转换为小写的字母
public MyString toLowerCase() {
//创建一个新的字符数组来存放转变后的字符
char[] chars = new char[length()];
for(int i=0; i<length();i++){
char ch = data[i];
if(isUpperLetter(ch)){
//将大写的字母转换为小写的字母
chars[i] = (char) (ch + 32);
}else{
//不是字母就直接赋值
chars[i] = ch;
}
}
return new MyString(chars);
}
//转换为大写字母
public MyString toUpperCase() {
char[] chars = new char[length()];
for(int i=0; i<length();i++){
char ch = data[i];
if(isLowerLetter(ch)){
chars[i] = (char) (ch - 32);
}else{
chars[i] = ch;
}
}
return new MyString(chars);
}
//判断是否为小写的字母
public boolean isLowerLetter(char ch){
return ch >= 'a' && ch <= 'z';
}
//判断是否为大写的字母
public boolean isUpperLetter(char ch){
return ch >= 'A' && ch <= 'Z';
}
//比较两个字符串的大小
public int compareTo(MyString s){
int i = 0;
int j = 0;
//定义左右指针,如果比较两个字符,如果相等就增加,有一个不等就返回两字符的差值,
//如果有一个字符已经遍历过了最大的角标就返回两个字符串长度的差值
while(true) {
if(charAt(i) == s.charAt(j)){
i++;
j++;
if(i == length() && j == s.length()){
return 0;
}
if(i < length() && j>=s.length() || i >= length() && j < s.length()){
return length() - s.length();
}
}else{
return charAt(i) - s.charAt(j);
}
}
}
//打印字符串
public void show() {
for(int i=0;i<length();i++){
System.out.print(data[i]);
}
System.out.println();
}
//根据字符索引返回字符
public char charAt(int index) {
return data[index];
}
//将MyString的类型的字符串赋值到data中
public MyString(MyString str) {
data = new char[str.length()];
for(int i=0; i<str.length();i++){
data [i] = str.charAt(i);
}
}
//返回字符数组的长度
public int length() {
return data.length;
}
}
概述:多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。其中,多个类可以称为子类,单独那一个类称为父类、超类(superclass)或者基类。
继承描述的是事物之间的所属关系,这种关系是: is-a
的关系。父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
定义:继承就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
class Student extends Person{
void study() {
System.out.println("study....");
}
}
class Worker extends Person{
void work() {
System.out.println("work....");
}
}
class Teacher extends Person {
void teach() {
System.out.println("teach....");
}
}
class Person {
String name;
int age;
}
总结:把多个事物中重复性的内容进行抽取,并生成另外一个类,该类就是其他类父类,其他类称之为子类,父类与子类之间用extends关键字来标明。
继承的好处
单继承与多继承
在现实生活中,父与子一对多关系,子与父是一对一的关系,使用继承时,除了要考虑类与类之间重复的内容之外,更重要的是去考虑关系,必须是一种 is-a
关系。
Java中 类与类之间只能支持单继承, 接口与接口之间可以支持多继承。
继承体系
既然有了单继承,也有了父子关系,爷孙关系,曾祖父-重孙,继承出现了层级,继承体系。
注意的问题
子父类中,成员变量的特点
this
来获取子类的成员变量,使用super
来获取父类空间中的父类变量。public class Sample {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println(zi.num);
zi.show();
}
}
class Fu {
int num = 10;
}
//父类 超类 基类
class Zi extends Fu {
int num = 20;
void show() {
int num = 30;
System.out.println(num + "," + this.num + "," + super.num);
}
}
小结:
this()
调用本类中其他构造函数的话,默认第一句是隐藏的 super()
public class Sample {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
class Fu {
int num;
Fu() {
System.out.println("Fu show..." + num);
num = 4;
}
}
class Zi extends Fu {
Zi() {
super(); //调用父类的无参构造函数 默认是隐藏的
//父类的构造函数一旦执行完毕 则紧接着执行子类成员变量的显示初始化
System.out.println("Zi show..." + num);
}
}
/*
结果:
Fu show...0
Zi show...4
*/
this(参数列表)
,在使用该构造函数创建对象时会调用该类的其他构造函数来调用父类的构造函数。public class Sample {
public static void main(String[] args) {
Zi zi1 = new Zi();
Zi zi2 = new Zi(1);
Zi zi3 = new Zi(1,2);
}
}
class Fu {
int num;
Fu() {
System.out.println("Fu show...");
}
}
class Zi extends Fu {
Zi() {
this(1);
System.out.println("Zi show ... 0");
}
Zi(int a) {
this(1,2);
System.out.println("Zi show ... 1");
}
Zi(int a , int b) {
super();
System.out.println("Zi show ... 2");
}
}
/*
结果:
Fu show...
Zi show ... 2
Zi show ... 1
Zi show ... 0
Fu show...
Zi show ... 2
Zi show ... 1
Fu show...
Zi show ... 2
*/
小结:
- 在构造函数中,第一句要么是this(),要么是super()
- 不存在每一个构造函数第一句都是this(),否则递归调用
- 存在每一个构造函数第一句都是super(),构造函数之间不调用
- this()与super()本身不冲突的,如果构造函数之间有调用关系,那么最后一个被调用的构造函数就不能再回调,那么其第一句就不能是this(),只能是super()
- 如果父类中,没有无参构造函数的存在,只有有参数的构造函数的话,那么子类中默认的super() 就调用不到父类无参构造函数!引发错误!
public class Sample {
public static void main(String[] args) {
Zi zi1 = new Zi();
Zi zi2 = new Zi(3);
Zi zi3 = new Zi(5,6);
}
}
class Fu {
int num;
Fu () {
System.out.println("Fu constructor... 0 " + "num = " + num);
num = 10;
}
Fu (int a) {
System.out.println("Fu constructor... 1 " + "num = " + num);
num = a;
}
}
class Zi extends Fu {
int num = 10;
Zi() {
System.out.println("Zi constructor... 0 " + "num = " + num + " fu num = " +super.num);
}
Zi(int a) {
this(a,0);
System.out.println("Zi constructor... 1 " + "num = " + num + " fu num = " +super.num);
}
Zi(int a, int b) {
super(a + b);
num = a + b;
System.out.println("Zi constructor... 2 " + "num = " + num + " fu num = " +super.num);
}
}
/*
结果:
Fu constructor... 0 num = 0
Zi constructor... 0 num = 10 fu num = 10
Fu constructor... 1 num = 0
Zi constructor... 2 num = 3 fu num = 3
Zi constructor... 1 num = 3 fu num = 3
Fu constructor... 1 num = 0
Zi constructor... 2 num = 11 fu num = 11
*/
子父类中,成员函数的特点
public class Sample {
public static void main(String[] args) {
Zi zi = new Zi();
zi.showA();
zi.showB();
zi.showC();
zi.showD();
}
}
class Fu {
void showA() {
System.out.println("Fu showA...");
}
void showC() {
System.out.println("Fu showC...");
}
private void showD() {
System.out.println("Fu showD...");
}
}
class Zi extends Fu{
void showB() {
System.out.println("Zi showB...");
}
@Override
void showC() {
System.out.println("Zi showC...");
}
void showD() {
System.out.println("Zi showD...");
}
}
/*
结果:
Fu showA...
Zi showB...
Zi showC...
Zi showD...
*/
上述第三个情况,我们称之为是函数的重写/覆盖/Override
重写的作用:严格意义上而言,子类并非是父类的一个子集。子类的内容很大程度上,很多情况下,都是父类的一种扩展或增量,重写仅仅去保留了父类的功能声明,但是具体的功能内容由子类来决定!
重写的规则:
public class Sample {
public static void main(String[] args) {
Zi zi = new Zi();
zi.showC(10);
zi.showC(10,20);
}
}
//public > 默认 > protected > private
class Fu {
public void show(){
System.out.println("Fu show...");
}
public int showB() {
return -1;
}
public void showC(int a){
System.out.println("Fu showC...");
}
}
class Zi extends Fu {
void show() {
System.out.println("Zi show...");
}
public void showB() {
}
public void showC(int a,int b){
System.out.println("Zi showC....");
}
}
/*
Sample.java:13: 错误: Zi中的show()无法覆盖Fu中的show()
void show() {
^
正在尝试分配更低的访问权限; 以前为public
1 个错误
*/
/*
Sample.java:21: 错误: Zi中的showB()无法覆盖Fu中的showB()
public void showB() {
^
返回类型void与int不兼容
*/
子父类中,静态成员的特点
public class Sample {
public static void main(String[] args) {
ArrayList list = new ArrayList();
for (int i = 1; i <= 12; i++) {
list.add(0,i);
}
System.out.println(list);
System.out.println(list.get(3));
System.out.println(list.delete(3));
System.out.println(list);
Stack stack = new Stack();
for (int i = 1; i <= 5; i++) {
stack.push(i);
}
System.out.println(stack);
System.out.println(stack.pop());
System.out.println(stack);
Queue queue = new Queue();
for (int i = 1; i <= 5; i++) {
queue.enqueue(i);
}
System.out.println(queue);
System.out.println(queue.dequeue());
System.out.println(queue);
}
}
class ArrayList {
private int[] data;
private int size;
int capacity = 10;
public ArrayList() {
data = new int[capacity];
size = 0;
}
//在指定角标处加入元素e
public void add(int index,int e) {
//1.对index做合法性处理
if (index < 0 || index > size) {
System.out.println(">>>插入位置不合法!");
return;
}
//2.考虑扩容的问题
if (size == data.length) {
resize(data.length * 2);
}
//3.合法加入 别忘了移动元素 不合法 结束并提示
for (int i = size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
//删除指定角标处的元素 并返回
public int delete(int index) {
//1.角标的合法性
if (index < 0 || index >= size) {
System.out.println(">>>删除位置不合法!");
return -1;
}
//2.删除即可 别忘了移动元素
int ret = data[index];
for (int i = index + 1; i < size; i++) {
data[i - 1] = data[i];
}
//3.考虑缩容的问题
size--;
if (size == data.length / 4 && data.length > capacity) {
resize(data.length / 2);
}
return ret;
}
//获取指定角标处的元素
public int get(int index) {
//1.考虑角标的合法性
if (index < 0 || index >= size) {
System.out.println(">>>获取位置不合法!");
return -1;
}
//2.直接返回
return data[index];
}
//扩容+缩容的操作
private void resize(int newlength) {
int[] newData = new int[newlength];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public String toString() {
//如果size == 0 返回 []
//如果不是[开始 拼接数字 每一个数字后面有个, 但是最后一个数字后面拼接]
String s = "[";
if (isEmpty()) {
s += "]";
} else {
for (int i = 0; i < size; i++) {
if (i == size - 1) {
s += data[i] + "]";
} else {
s += data[i] + ",";
}
}
}
return s;
}
}
class Stack extends ArrayList{
public Stack() {
super();
}
public void push(int e) {
add(size(),e);
}
public int pop() {
return delete(size() - 1);
}
public int peek() {
return get(size() - 1);
}
}
class Queue extends ArrayList {
public Queue() {
super();
}
public void enqueue(int e) {
add(size(),e);
}
public int dequeue() {
return delete(0);
}
public int front() {
return get(0);
}
public int rear() {
return get(size() - 1);
}
}
/*
结果:
[12,11,10,9,8,7,6,5,4,3,2,1]
9
9
[12,11,10,8,7,6,5,4,3,2,1]
[1,2,3,4,5]
5
[1,2,3,4]
[1,2,3,4,5]
1
[2,3,4,5]
*/
final:用于修饰不可改变内容,可以用于修饰类、函数和变量。
final修饰变量
表示该变量的值不可被改变
变量主要分为两种,基本数据类型、引用数据类型的变量
public class Sample {
public static void main(String[] args) {
final Demo d = new Demo();
//d = new Demo(); //error
//d.num = 30;//error
d.haha = 40;//ok
}
}
class Demo {
public final int num = 10;
public int haha = 20;
}
一般而言,当我们在定义常量(用变量名+final来表示),并定义成静态变量
public static final 数据类型 变量名 = 常量数据;
对于常量的变量名起名规则为 全部单词大写 单词与单词之间用下划线分隔
public static final int MAX_VALUE = 100;
final修饰函数
如果某一个类中的函数,不想让其子类去重写的话,该函数就可以声明为final类型
final修饰类
表示该类不能被继承
概述:
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
定义:
抽象方法
使用 abstract
关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
抽象类
如果一个类包含抽象方法,那么该类必须是抽象类。
定义格式:
public abstract class Animal {
public abstract void run();
}
抽象方法:当我们将多个事物的共同行为(方法)进行抽取并封装到另外一个类中时,发现在该类中,这些方法的具体执行内容无法确定,只能由这些子类来决定该方法的具体执行,那么在该类中,将这些抽取来的方法仅保留方法声明,但不保留方法体即可,那么该方法就是抽象方法,用abstract关键字来修饰,既然有了抽象方法的存在,那么具有抽象方法的类也被称之为抽象类,也必须用abstract修饰。抽象类不能创建对象,只有其实现子类能够创建对象。
抽象类的特点:
抽象类的细节问题:
- 抽象关键字abstract不能与以下关键字共存
- final:final修饰类,表示该类不能被继承;final修饰函数时,表示函数不能被重写;不能与absteact共存,抽象类本就是父类,并且其中的抽象函数就等着被子类重写。
- private:private修饰函数,表示函数被私有,不能被子类继承;不能与absteact共存,抽象函数就等着被子类重写。
- static:static修饰的函数,属于类的,随着类的加载从而被加载方法区中,和对象没有关系了,可以直接用类来调用静态成员,如果抽象函数被静态修饰,被类调用时没意义。
概述:
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。
接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成**.class文件,但一定要明确它并不是类,而是另外一种引用数据类型**。
引用数据类型:数组,类,接口。
接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
接口中的变量和函数就会有一些特殊的含义
默认方法的使用
定义接口:
public interface LiveAble {
public default void fly(){
System.out.println("天上飞");
}
}
定义实现类:
public class Animal implements LiveAble {
// 继承,什么都不用写,直接调用
}
定义测试类:
public class InterfaceDemo {
public static void main(String[] args) {
// 创建子类对象
Animal a = new Animal();
// 调用默认方法
a.fly();
}
}
/*
输出结果:
天上飞
*/
定义接口:
public interface LiveAble {
public default void fly(){
System.out.println("天上飞");
}
}
定义实现类:
public class Animal implements LiveAble {
@Override
public void fly() {
System.out.println("自由自在的飞");
}
}
定义测试类:
public class InterfaceDemo {
public static void main(String[] args) {
// 创建子类对象
Animal a = new Animal();
// 调用重写方法
a.fly();
}
}
/*
输出结果:
自由自在的飞
*/
静态方法的使用
定义接口:
public interface LiveAble {
public static void run(){
System.out.println("跑起来~~~");
}
}
定义实现类:
public class Animal implements LiveAble {
// 无法重写静态方法
}
定义测试类:
public class InterfaceDemo {
public static void main(String[] args) {
// Animal.run(); // 【错误】无法继承方法,也无法调用
LiveAble.run(); //接口的静态方法使用接口名来调用接口中的静态方法
}
}
/*
输出结果:
跑起来~~~
*/
私有方法的使用
如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到私有方法中,供默认方法去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。
public interface LiveAble {
default void func(){
//默认方法可以调用接口中的私有方法和静态的私有方法
func1();
func2();
}
private void func1(){
System.out.println("跑起来~~~");
}
private void func2(){
System.out.println("跑起来~~~");
}
}
优先级的问题
当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执行父类的成员方法。代码如下:
interface A {
public default void methodA(){
System.out.println("AAAAAAAAAAAA");
}
}
class D {
public void methodA(){
System.out.println("DDDDDDDDDDDD");
}
}
class C extends D implements A {
// 未重写methodA方法
}
public class Test {
public static void main(String[] args) {
C c = new C();
c.methodA();
}
}
/*
输出结果:
DDDDDDDDDDDD
*/
接口与类、接口之间的关系
子接口重写默认方法时,default关键字可以保留。
子类重写默认方法时,default关键字不可以保留。
接口中的特点总结
接口与抽象类的区别
相同点:
不同点:
概述:
多态是继封装、继承之后,面向对象的第三大特性。
生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。
定义:是指同一行为,具有多个不同表现形式
多态体现的格式:
父类类型 变量名 = new 子类对象;
变量名.方法名();
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
Fu f = new Zi();
f.method();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。
代码如下:
public abstract class Animal {
public abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
public class Test {
public static void main(String[] args) {
//多态形式,创建对象,向上转型
Animal cat = new Cat();
//先找父类中是否有eat()这个方法,没有编译报错,有的话调用子类中定义的方法体
cat.eat();
Animal dog = new Dog();
dog.eat();
}
}
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。
public abstract class Animal {
public abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Cat c = new Cat();
Dog d = new Dog();
// 调用showCatEat
showCatEat(c);
// 调用showDogEat
showDogEat(d);
/*
以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代
而执行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
public static void showCatEat (Cat c){
c.eat();
}
public static void showDogEat (Dog d){
d.eat();
}
public static void showAnimalEat (Animal a){
a.eat();
}
}
优点:
面向父类/接口编程、关注的是父类/接口的能力、忽略了子类类型、可以使程序编写的更简单,并有良好的扩展。
缺点:
无法使用子类特有的属性和方法。
向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型,向上转型后,该类只可以使用父类中声明的方法,但调用的是子类中重写父类的方法,不可以调用子类中自己定义的方法。
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型,向下转型后可以调用子类中定义的方法。
子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;
为什么用转型
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void watchHouse() {
System.out.println("看家");
}
}
public class Test {
public static void main(String[] args) {
// 向上转型,只能调用父类中定义的方法,不可以调用子类中定义的方法
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
}
}
转型的异常
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException
,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。
为了避免ClassCastException
的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
定义:将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
成员内部类 :定义在类中方法外的类。
class 外部类 {
class 内部类{
}
}
在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。比如,汽车类 Car 中包含发动机类Engine
,这时, Engine
就可以使用内部类来描述,定义在成员位置。
class Car {
//外部类
class Engine {
//内部类
}
}
创建内部类对象格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
外部类名.内部类名 对象名 = new 外部类型.内部类型();
Outter.InnerB b=new Outter().new InnerB();
Outter.InnerB b=new Outter.InnerB();
public class Person {
private boolean live = true;
class Heart {
public void jump() {
// 直接访问外部类成员
if (live) {
System.out.println("心脏在跳动");
} else {
System.out.println("心脏不跳了");
}
}
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
}
public class InnerDemo {
public static void main(String[] args) {
// 创建外部类对象
Person p = new Person();
// 创建内部类对象
Heart heart = p.new Heart();
// 调用内部类方法
heart.jump();
// 调用外部类方法
p.setLive(false);
// 调用内部类方法
heart.jump();
}
}
/*
输出结果:
心脏在跳动
心脏不跳了
*/
内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和
$
符号 。
比如,Person$Heart.class
匿名内部类 :是内部类的简化写法。它的本质是一个 带具体实现的 父类或者父接口的 匿名的 子类对象。开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作:
而匿名内部类可以简化上面的方法,为我们调用方法提供快捷的方式。
前提
匿名内部类必须继承一个父类或者实现一个父接口。
格式
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
举例
public abstract class FlyAble{
public abstract void fly();
}
public class InnerDemo {
public static void main(String[] args) {
/*
1.等号右边:是匿名内部类,定义并创建该接口的子类对象
2.等号左边:是多态赋值,接口类型引用指向子类对象
*/
FlyAble f = new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
};
//调用 fly方法,执行重写后的方法
f.fly();
}
}
通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:
public class InnerDemo2 {
public static void main(String[] args) {
/*
1.等号右边:定义并创建该接口的子类对象
2.等号左边:是多态,接口类型引用指向子类对象
*/
FlyAble f = new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
};
// 将f传递给showFly方法中
showFly(f);
}
public static void showFly(FlyAble f) {
f.fly();
}
}
以上两步,也可以简化为一步,代码如下:
public class InnerDemo3 {
public static void main(String[] args) {
/*
创建匿名内部类,直接传递给showFly(FlyAble f)
*/
showFly( new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
});
}
public static void showFly(FlyAble f) {
f.fly();
}
}
定义:包其实相当于操作系统的文件夹
好处:
定义包名:
.
分隔,不能以.
开头包的声明:
package com.openlab.student.entity;
必须位于类的第一行非注释语句
包的导入:
import java.util.Scanner;
:类的完全限定名
import java.util.*;
:导入util包下的所有java类,但是不会导入子包中的类
导入的类重名,来自不同的包,需要显式的写出
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限。
public | protected | default | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中(子类与无关类) | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ |
public>protected>default>private
编写代码时,如果没有特殊的考虑,建议这样使用权限:
不加权限修饰符,其访问能力与default修饰符相同
枚举可以当成数据类型来用,管理一系列的常量(限定取值)
由来:
枚举出来之前都是拿class或者interface来组织管理基本数据类型的常量
缺点:只要数据类型合适,就会编译通过,不考虑实际的业务,可能造成错误
枚举:本质是Enum的子类,不能再继承别的类,可以限定取值
枚举的定义:
public enum Role2 {
ROLE_NOMAL,
ROLE_VIP,
ROLE_SUPER_VIP
}
枚举可以当成数据类型来使用
private Role2 role;
枚举值的比较 equals
或者==
都可以
Role2 r=user.getRole();
if(r==Role2.ROLE_SUPER_VIP) {
System.out.println("超级用户。。。。。。。。。。。。");
}else if(r==Role2.ROLE_NOMAL) {
System.out.println("普通用户。。。。。。。。。。。。");
}
switch(r) {
//byte int short String(1.7+),enum
case ROLE_SUPER_VIP:
System.out.println("超级用户。。。。。。。。。。。。");
break;
case ROLE_NOMAL:
System.out.println("普通用户。。。。。。。。。。。。");
break;
case ROLE_VIP:
System.out.println("VIP用户。。。。。。。。。。。。");
break;
}
java.lang.Object
类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候,最终找的父类就是Object,如果一个类没有特别指定父类, 那么它默认继承自Object类。
主要方法
public boolean equals(Object obj)
:指示其他某个对象是否与此对象相等public String toString()
:返回该对象的字符串表示public final native Class> getClass()
:返回的是对象所属类的字节码对象(类的完全限定名)public native int hashCode()
:返回对象的哈希值,默认返回对象的地址,允许子类重写protected void finalize()
:当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法public boolean equals(Object obj)
:指示其他某个对象是否与此对象相等,默认使用==
判断两个对象是否相等,其实是比较对象的地址是否相等。
注意:参数是Object 需要向下转型,之前需要判断数据类型是否属于当前类型,允许子类自定义比较规则,通过子类重写equals()方法,自定义比较的规则。
实例
import java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
// 如果对象地址一样,则认为相同
if (this == obj)
return true;
// 如果参数为空,或者类型信息不一样,则认为不同
if (obj == null || !(obj instanceof Person))
return false;
// 向下转型
Person person = (Person) obj;
// 要求基本类型相等,并且将引用类型交给java.util.Objects类的equals静态方法取用结果
return age == person.age && Objects.equals(name, person.name);
}
}
String类中的equals()方法
String类重写类父类Object类中的equals()方法,它比较的是两个对象的值是否相等。
源码如下:
public boolean equals(Object anObject) {
//首先判断两个对象的地址是否相等
if (this == anObject) {
return true;
}
//在判断两个传入类是否为String 类,并向下转型后比较值是否相等
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
public String toString()
:把当前对象转换成字符串形式,未重写时,返回:类的完全限定名+@+哈希值的十六进制字符串
,可以继承,可以重写。
源码如下:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
由于toString方法返回的结果是内存地址,而在开发中,经常需要按照对象的属性得到相应的字符串表现形式,因此也需要重写它。
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
public final native Class> getClass()
:返回的是对象所属类的字节码对象(类的完全限定名)。
native: 本地的,修饰的方法,没有方法体,但是不代表没有实现,通常是由C/C++来实现的。
Class类 :字节码文件的类型,也是对象,类只加载一次。
由于String类的对象内容不可改变,所以每当进行字符串拼接时,总是会在内存中创建一个新的对象。例如:
public class StringDemo {
public static void main(String[] args) {
String s = "Hello";
s += "World";
System.out.println(s);
}
}
根据这句话分析我们的代码,其实总共产生了三个字符串,即"Hello"
、"World"
和"HelloWorld"
。引用变量s首先指向Hello
对象,最终指向拼接出来的新字符串对象,即HelloWord
。
由此可知,如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,既耗时,又浪费空间。为了解决这一问题,可以使用java.lang.StringBuilder
和java.lang.StringBuffer
类。
StringBuffer
和StringBuilder
类都继承于AbstractStringBuilder
,内部使用基本的char[] value
字符数组来保存字符串,父类为StringBuffer
和StringBuilder
提供基本的方法,因此StringBuffer
和StringBuilder
类的方法基本相同。
StringBuffer
和StringBuilder
类的不同处StringBuffer类
StringBuilder类
常用构造方法
public StringBuilder()
:构造一个空的StringBuilder容器。public StringBuilder(String str)
:构造一个StringBuilder容器,并将字符串添加进去。常用方法
public StringBuilder append(...)
:添加任意类型数据的字符串形式,并返回当前对象自身。public String toString()
:将当前StringBuilder对象转换为String对象。public StringBuffer insert(int offset, String str)
:指定位置插入字符串。public StringBuffer reverse()
:翻转该字符串。public String substring(int start, int end)
:截取指定开始 索引和结束索引位置的字符串。更多:第一部分语法基础