因为Java程序必须运行在JVM之上,所以,第一件事情就是安装JDK。
搜索JDK 13,确保从Oracle的官网下载最新的稳定版JDK:
安装完JDK后,需要设置一个JAVA_HOME
的环境变量,它指向JDK的安装目录。在Windows下,它是安装目录,类似:
C:\Program Files\Java\jdk-13
在Mac下,它在~/.bash_profile
里,它是:
export JAVA_HOME=`/usr/libexec/java_home -v 13`
然后,把JAVA_HOME
的bin
目录附加到系统环境变量PATH
上。在Windows下,它长这样:
Path=%JAVA_HOME%\bin;<现有的其他路径>
在Mac下,它在~/.bash_profile
里,长这样
export PATH=$JAVA_HOME/bin:$PATH
把JAVA_HOME
的bin
目录添加到PATH
中是为了在任意文件夹下都可以运行java
。打开命令提示符窗口,输入命令java -version
,如果一切正常,你会看到如下输出:
┌────────────────────────────────────────────────────────┐ │Command Prompt - □ x │ ├────────────────────────────────────────────────────────┤ │Microsoft Windows [Version 10.0.0] │ │(c) 2015 Microsoft Corporation. All rights reserved. │ │ │ │C:\> java -version │ │java version "13" ... │ │Java(TM) SE Runtime Environment │ │Java HotSpot(TM) 64-Bit Server VM │ │ │ │C:\> │ │ │ │ │ └────────────────────────────────────────────────────────┘
如果你看到的版本号不是13
,而是12
、1.8
之类,说明系统存在多个JDK,且默认JDK不是JDK 13,需要把JDK 13提到PATH
前面。
如果你得到一个错误输出:
┌────────────────────────────────────────────────────────┐ │Command Prompt - □ x │ ├────────────────────────────────────────────────────────┤ │Microsoft Windows [Version 10.0.0] │ │(c) 2015 Microsoft Corporation. All rights reserved. │ │ │ │C:\> java -version │ │'java' is not recognized as an internal or external comm│ │and, operable program or batch file. │ │ │ │C:\> │ │ │ │ │ │ │ └────────────────────────────────────────────────────────┘
简要介绍下类、对象、方法和实例的概念。
第一个程序HelloWorld
/** * 可以用来自动创建文档的注释 */ public class Hello { public static void main(String[] args) { // 向屏幕输出文本: System.out.println("Hello, world!"); /* 多行注释开始 注释内容 注释结束 */ } } // class定义结束
按所属的数据类型划分:基本类型的变量和引用类型的变量。
按被声明的位置划分:局部变量(方法或语句块内部定义的变量)和成员变量(方法外部、类的内部定义的变量)
在Java中,变量必须先定义后使用,在定义变量的时候,可以给它一个初始值。例如:
int x = 1;
基本数据类型是CPU可以直接进行运算的类型。Java定义了以下几种基本数据类型:
基本数据类型转换:
强制类型转换:
方法(又叫函数)就是一段特定功能的代码块。方法提高程序的复用性和可读性。
方法的格式
// 语法: // 访问权限修饰符[其他的修饰符如static]返回值类型 方法名(参数类型1形参1,参数类型2形参2,...){// 形参列表 // 方法体 return 返回值; }
实际参数:就是实际参与运算的。
形式参数:就是方法定义上的,用于接受实际参数的;
参数类型:就是参数的数据类型
参数名:就是变量名
方法体语句:就是完成功能的代码
注意:
1.若当前方法中不要使用形参,那么形参列表可以为空
2.实参和形参的类型要相互兼容,且:实参的取值范围要小于或者等于形参类型的取值范围
在调用方法中,如果我们定义的方法有参数,就必须在调用方法的同时传入这个值,即给当前方法中的参数赋值,而这个传入的值我们称为实记参数,也就是实参
实参:传入的参数值
形参:接受实参传过来的值
注意:实参名与形参名可以相同,也可以不同
小结:形参就是一个变量,实参就是一个值,传参就是把一个值给一个形参赋值
return: 结束方法的
返回值:就是功恩的结果,由return带给调用者。
注意:
练习:
判断任意给定年份是否是闰年
public class test { public static void main(String[] args){ boolean bool = isRunNian(2017); if(bool){ System.out.println("是闰年"); }else{ System.out.println("是平年"); } } public static boolean isRunNian(int year){ if((year%4==0 && year%100!=0) || year%400==0){ return true; }else{ return false; } } }
根据传入的参数数量打印直角三角形
public class printTriangle { public static void main (String[] args){ Scanner input = new Scanner(System.in); int num = input.nextInt(); print(num); } public static void print(int line) { for(int i=1 ; i<=line ; i++) { // 控制行数 for(int j=i;j<line;j++) { System.out.print(" "); } for(int k=1; k<=i*2-1; k++) { System.out.print("*"); } System.out.println(); } } }
方法重载:overloading method
在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义;
返回值不能作为重载的条件
// 如: public void method(int a){...} public void method(char b){...}
数组:一组能够存储相同数据类型值的变量的集合
当我们有个一组相同类型的数据需要存储,如果次是使用单个变量的来存储,我们将要定义若干个变量名,这样将会非常繁琐,并不利于维护
四种:
使用默认的初始值来初始化数组中的每一个数组
语法:数组元素类型 [] 数组名 = new 数组元素类型[数组中元素的个数(数组的长度)]
如:int [] scores = new int[3];
先声明,然后再赋予默认的初始值
语法:数组元素类型 [] 数组名;
数组名 = new 数组元素类型[数组中元素的个数(数组的长度)];
如:int [] scores;
scores = new int[3];
先声明,然后在使用特定的值进行初始化
语法:数组元素类型 [] 数组名 = new 数组元素类型 [] {元素1,元素2,…};
如:int [] scores = new int[]{56,78,98};
将第三种写法可以简化为(使用数组常量值给数组进行赋值)
语法:数组袁术类型 [] 数组名 = {元素1,元素2,…};
如:int [] scores = {56,78,98}
遍历:依次语出数组中的每一个元素
通过下标来访问数组的元素
下标: 从0开始,到数组长度-1
遍历方法一:普通的for循环
语法:for(int i = 0; i< 数组的长度; i++){
// i: 循环变量,同样:也是数组的下标(取值范围[0,数组的长度])
数组中元素的类型 变量 = 数组名[i];
}
遍历方式二:使用增强for循环【foreach循环】
语法:for(数组中元素的类型 变量:数组名){
数组中元素的类型 临时变量 = 变量;
}
猜数游戏,从键盘中任意输入一个数据,判断数列中是否包含此数
public class test2 { // 猜数游戏,从键盘中任意输入一个数据,判断数列中是否包含此数 public static void main(String[] args) { int[] nums = {2,32,23,54,60}; Scanner input = new Scanner(System.in); System.out.println("请输入你要猜的数:(60以内)"); int userNum = input.nextInt(); boolean flag = false; for(int i:nums){ if(userNum == i){ flag = true; break; } } if(flag) { System.out.println("恭喜你猜对了"); }else{ System.out.println("猜错了继续努力"); } } }
打印正三角形
1.一起来参加屌丝程序员大赛把,有3个班级各3名学员参赛,记录每个学员的成绩,并计算每个班的平均分。
public class test3 { public static void main(String[] args){ int [][] scores = {{99,78,50},{90,86},{67,87,56}}; int classLen = scores.length; for(int[] i:scores){ int sum = 0; for(int j:i){ sum += j; } int avg = sum / i.length; System.out.println("平均成绩:"+avg); } } }
求最大值与最小值的算法
最大值:在一个数列中找出最大的数
public class test4 { public static void main(String[] args){ int[] nums = {1,2,3,4,5,6}; int max = max(nums); System.out.println(max); } public static int max(int [] nums){ int max = nums[0]; int len = nums.length; for(int i = 1;i<len;i++){ if(max < nums[i]){ nums[i] = nums[i] + max; max = nums[i] - max; nums[i] = nums[i] - max; } } return max; } }
最小值:在一个数列中找出最小的数
public class test4 { public static void main(String[] args){ int[] nums = {1,2,3,4,5,6}; int min = min(nums); System.out.println(min); } public static int max(int [] nums){ int min = nums[0]; int len = nums.length; for(int i = 1;i<len;i++){ if(min > nums[i]){ nums[i] = nums[i] + min; min = nums[i] - max; nums[i] = nums[i] - min; } } return min; } }
冒泡排序算法
冒泡排序算法的运作如下:(从后往前)
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,知道没有任何一对数字需要比较。
相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法
public class test5 { public static void main(String[] args){ int[] nums = {10,21,3,34,5,19}; int len = nums.length-1; for(int i = 0; i < len;i++){ for(int j = 0; j < len-i;j++){ if(nums[j] > nums[j+1]){ nums[j+1] = nums[j]+nums[j+1]; nums[j] = nums[j+1] - nums[j]; nums[j+1] = nums[j+1] - nums[j]; } } } for(int n:nums){ System.out.println(n); } } }
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法
public class test6 { public static void main(String[] args){ int[] nums = {10,21,3,34,5,19}; int len = nums.length; int minIndex; for(int i=0;i<len;i++){ minIndex = i; for(int j=i+1;j<len-1;j++){ if(nums[minIndex] > nums[j]){ minIndex = j; } } if(minIndex != i){ if(nums[i] > nums[minIndex]){ nums[minIndex] = nums[i]+nums[minIndex]; nums[i] = nums[minIndex] - nums[i]; nums[minIndex] = nums[minIndex] - nums[i]; } } } for(int n:nums){ System.out.println(n); } } }
直接插入排序算法
(从后向前找到合适位置后插入)
**基本思想:**每步将一个待排序的记录,按其顺序码大小插入到前面已经排好序的子序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。
public class test7 { public static void main(String[] args){ int[] nums = {10,21,3,34,5,19}; for(int i = 1;i<nums.length;i++){ int temp = nums[i]; int j; for(j = i-1;j>=0;j--){ if(nums[j] > temp){ nums[j+1] = nums[j]; }else{ break; } } if(temp != nums[j+1]){ nums[j+1] = temp; } } for(int n:nums){ System.out.println(n); } } }
二分法查找(折半查找):前提是在已经排好序的数组中,通过将待查找的元素与中间索引值对应的元素进行比较,若大于中间索引值对应的元素,去右半部分查找,否则,去左半部分查找。依次类推。知道找到为止,找不到返回一个负数。
public class test8 { public static void main(String[] args){ int[] nums = {1,2,3,4,5,6}; int index = binarySearch(nums,3); System.out.println(index); } public static int binarySearch(int[] nums, int key){ int start = 0; int end = nums.length-1; while (start<=end){ int middle = (start+end)/2; if(nums[middle]>key){ end = middle-1; }else if(nums[middle]<key){ start = middle+1; }else{ return middle; } } return -1; } }
Arrays工具类:用来操作数组(比如排序和搜索)的各种方法
常用方法:
使用二分法查找
Arrays.binarySearch(int [] array,int value);
数组内容转成字符串的形式输出
Arrays.toString(int [] array);
数组排序
Arrays.sort(int[] array);
复制指定的数组
Arrays.copyOf(int[] array,int length)
Arrays.copyOf(int[] array,int form,int to)
System.arraycopy(Object src, int s rcPos,Object dest,int destPos,int length)
判断两个数组是否相等
Arrays.equels();
使用指定元素填充数组
Arrays.fill()
一、双色球彩票玩法
玩法说明:
双色求投注区分为红色号码区和蓝球号码区,红球号码范围为0133,蓝球号码范围为0116。双色球每期从33个红球中开出6个号码,从16个蓝球中 开出1个号码作为中将号码,双色球玩法即是竞猜开奖号码的6个红球号码和1个蓝球号码,顺序不限
二、案例分析
1、如何产生蓝球和红球?
2、如何接受用户选号?
3、如何验证是否中奖?
4、公布本期中将号码?
三、实现步骤:
1、整体实现思路
2、随机取值不重复算法(系统和用户)
3、判断是否中奖的逻辑
4、结果输出
import java.util.Arrays; import java.util.Random; import java.util.Scanner; public class test9 { public static void main(String[] args){ // 定义相关变量 int[] userRedBall = new int[6];// 用户选择的红球号码 int[] sysRedBall = new int[6];// 系统生成的红球号码 int userBlueBall = 0;// 用户选择的蓝球号码 int sysBlueBall = 0;// 系统生成的蓝球号码 int redCount = 0;// 记录用户选择正确的红球 int blueCount = 0;// 记录用户选择正确的蓝球 int [] redBall = new int[33]; //用于存储1-33的红球号码 for(int i = 0;i<redBall.length;i++){ redBall[i] = i+1; } // 游戏开始提示 System.out.println("双色球游戏开始,good luck"); System.out.println("请问你是要机选还是要手选:(1:机选,2:首选)"); Scanner input = new Scanner(System.in); Random r = new Random(); boolean flag = true; while(flag){ int isAuto = input.nextInt(); switch (isAuto){ case 1: // 机选 computerSelect(redBall,userRedBall); userBlueBall = r.nextInt(16)+1; flag = false; break; case 2: // 手选 System.out.println("请选择6个红球号码(1-33)"); for(int i=0;i<userRedBall.length;i++){ userRedBall[i] = input.nextInt(); } System.out.println("请选择1个蓝球号码(1-16)"); userBlueBall = input.nextInt(); flag = false; break; default: System.out.println("请问你是要机选还是要手选:(1:机选,2:首选)"); break; } } // 系统随机生成号码 computerSelect(redBall,sysRedBall); sysBlueBall = r.nextInt(16)+1; for(int i=0;i<userRedBall.length;i++){ for(int j=0;j<sysRedBall.length-redCount;j++){ if(userRedBall[i] == sysRedBall[j]){ int temp = userRedBall[i]; userRedBall[i] = sysRedBall[userRedBall.length-1-redCount]; sysRedBall[userRedBall.length-1-redCount] = temp; redCount++; break; } } } if(userBlueBall == sysBlueBall){ blueCount = 1; } if(blueCount==0 && redCount<=3){ System.out.println("没中奖"); }else if(blueCount==1 && redCount<3){ System.out.println("中了六等级,5块钱"); }else if(blueCount==1 && redCount==3 || blueCount==0 && redCount==4){ System.out.println("中了五等级,10块钱"); }else if(blueCount==1 && redCount==4 || blueCount==0 && redCount==5){ System.out.println("中了四等级,200块钱"); }else if(blueCount==1 && redCount==5){ System.out.println("中了三等级,3000块钱"); }else if(blueCount==0 && redCount==6){ System.out.println("中了三等级,150w块钱"); }else if(blueCount==1 && redCount==6){ System.out.println("中了三等级,500w块钱"); }else{ System.out.println("系统有误,中奖无效"); } // 系统号码 System.out.println("本期中奖的红球"); Arrays.sort(sysRedBall); System.out.println(Arrays.toString(sysRedBall)); System.out.println("本期中奖的蓝球"); System.out.println(sysBlueBall); // 用户号码 System.out.println("你选择的红球"); Arrays.sort(userRedBall); System.out.println(Arrays.toString(userRedBall)); System.out.println("你选择的蓝球"); System.out.println(userBlueBall); } public static void computerSelect(int[] redBall,int[] userRedBall){ Random r = new Random(); int index = -1; for(int i=0;i<userRedBall.length;i++){ index = r.nextInt(redBall.length-i); userRedBall[i] = redBall[index]; //交换位置 int temp = redBall[index]; redBall[index] = redBall[redBall.length-1-i]; redBall[redBall.length-1-i] = temp; } } }
一、什么是面向对象:
1.面向对象是一种编程思想。
2.面向对象是一种思考问题的思维方式。
二、简历面向对象思维方式:
1.先整体,再局部
2.先抽象,再具体
3.能做什么,再怎么作
三、如何学习面向对象
1.掌握一门面向对象语言的语法
2.熟悉面向对象的设计原则
3.熟悉面向对象设计模式
类便是一个共性的产物,是一个综合的特征,而对象,是一个个性的产物,是一个个体的特征。
类由属性和方法组成
属性:就相当于一个个的特征
方法:就相当于人的一个个的行为,例如:说话,吃饭,唱歌,睡觉
在Java中可以使用一下的语句定义一个类:
class 类名称{
属性名称;
返回值类型 方法名称(){}
}
对象的定义:
一个类要想真正的进行操作,则必须依靠对象,对象的定义格式如下:
类名称 对象名称 = new 类名称();
按照以上的格式就可以产生对象了。
如果要想访问类中的属性或方法(方法的定义)
则可以依靠一下的语法形式:
访问类中的属性:
对象.属性;
调用类中的方法:
对象.方法();
注意:如果一个没有申请内存空间的对象,会报空指针异常
一、封装性的概念
封装性是面向对象思想的三大特征之一。
封装就是隐藏实现细节,仅对外提供访问接口。
封装有:属性的封装、方法的封装、类的封装、组件的封装、模块化的封装、系统级封装…
二、封装的好处
模块化
信息隐藏
代码复用
插件化易于调试
具有安全性
封装的缺点:会影响执行效率
private关键字:访问权限修饰符,public表示共有的,private表示私有的,私有的属性或方法,只能在本类中访问
puclick class Test10{ public static void main (String() args){ } } class Person{ private String name; private int age; public void setName(String name){ this.name = name; } public String getName(){ return this.name; } }
在类中的位置不同
成员变量:在类中定义
局部变量:在方法中定义或者方法的参数
在内存中的位置不同
成员变量:在堆内存(成员变量属于对象,对象进堆内存)
局部变量:在栈内存(局部变量属于方法,方法进占内存)
生命周期不同
成员变量:随着对象的创建而存在,随着对象的销毁而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
初始化值不同
成员变量:有默认初始化值,引用类型为null
局部变量:没有默认初始化值,必须定义,赋值,然后才能使用
注意:局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则
什么是构造方法
构造方法的定义:
构造方法是在类型定义的,构造方法的定义格式:方法名称与类的名称相同,无返回值类型的声明。
对象的实例化语法:
Dog dog = new Dog();// new Dog后面有个括号,表示调用了方法,此时调用的方法就是构造方法了。
class Dog{ public Dog(){ System.out.printLn("构造方法执行了") } public Dog(String name,int age){ this.name = name; this.age = age; System.out.printLn("带两个参数的构造方法执行了") } }
在java基础中,this关键字是一个最重要的概念。使用this关键字可以完成一下操作:
public class valueDemo{ public static void main(String[] args){ int x = 10; method(x); System.out.println("x="+x); } public static void method(int mx){ mx = 20; } }
public class RefDemo{ public static void main(String[] args){ Duck d = new Duck(); method(d); System.out.println("Duck age ="+d.age); } public static void method(Duck duck){ duck.age = 5; } } class Duck{ int age = 2; // 省略封装 }
两个对象之间的一对一关系:
比如:
一个英雄(Hero)对一个兵器(Weapon)
public class test10 { public static void main(String[] args){ Hero hero = new Hero(); hero.setName("吕布"); hero.setAge(22); Weapon weapon = new Weapon(); weapon.setName("方天画戟"); hero.setWeapon(weapon); weapon.setHero(hero); } } class Hero{ private String name; private int age; private Weapon weapon; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setWeapon(Weapon weapon) { this.weapon = weapon; } public Weapon getWeapon() { return weapon; } } class Weapon{ private String name; private Hero hero; public void setName(String name) { this.name = name; } public String getName() { return name; } public Hero getHero() { return hero; } public void setHero(Hero hero) { this.hero = hero; } }
使用static关键字修饰一个属性
声明为static的关键字实质上就是一个全局变量
使用static关键字修饰一个方法
通常,在一个类中定义已给方法为static,那就是说,无需本类的对象即可调用此方法
使用static关键字修饰一个类(内部类)
它们仅能调用其他的static的方法。
它们只能访问static数据。
它们不能以任何方式引用this或super。
什么时候使用static?
所有对象共同的属性或方法,那么我们应该定义为静态的。
//主方法: public static void main(String[] args){ //代码块 }
public:共有的,最大的访问权限
static:静态的,无需创建对象
void:表示没有返回值,无需向JVM返回结果
main:方法名,固定的方法名
String[] args: 表示参数为字符串数组
普通代码块
直接写在方法中的代码块就是普通代码块
示例:
public class Demo1{ public static void main(String[] args){ { //普通代码块 String info = "局部变量-1"; System.out.println(info); } String info = "局部变量-2"; System.out.println(info); } }
构造块是在类中定义的代码块
class Demo{ { //代码块 System.out.println("构造块") } public Demo(){ System.out.println("构造方法") } }
在类中使用static声明的代码块称为静态代码块
在第一次使用的时候被调用(创建对象),只会执行一次,优于构造块执行
class Demo{ { System.out.println("构造块") } static{ System.out.println("静态代码块") } public Demo(){ System.out.println("构造方法") } }
同步代码块
单例设计模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
两种实现方式:
1.饿汉式:在第一次调用getInstance方法时,对象被创建,到程序结束后释放
class Singleton1{ private Singleton1(){} private static Singleton1 s = new Singleton1(); public static Singleton1 getInstance(){ return s; } public void print(){ System.out.println("测试方法") } }
2.懒汉式:在类被加载后,对象被创建,到程序结束
class Singleton2{ private Singleton2(){} private static Singleton2 s; public static Singleton2 getInstance(){ if(s==null){ s = new Singleton2(); } return s; } public void print(){ System.out.println("测试方法") } }
对象数组就是数组里的每个元素都是类的对象,赋值时先定义对象,然后将对象直接复制给数组。
示例:
Chicken[] cs = new Chicken[10];
使用对象数组实现多个Chicken的管理。
继承是从已有的类创建新类的过程
protected访问权限修饰符,在继承关系中使用,在父类中使用protected修饰的属性或方法可以被子类继承
创建子类对象时,父类的构造方法也会被调用,为什么?
因为子类要使用到父类的数据,那么就要通过父类的构造方法来初始化数据,
如果传见子类对象时使用过默认的构造方法,那么父类的默认构造方法也会被调用
如果创建子类对象时会调用父类的默认构造方法
class Dog{ protected name; protected age; public void eat(){ System.out.println("开始干饭了") } } class HomeDog extends Dog{ public void say(){ System.out.println("我是家狗") } }
继承小结
继承的好处:
继承的缺点
增强了类与类之间的耦合性
开发原则:高内聚,低耦合
在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想做一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
在子类和父类中,重写方法后,在调用时,已创建的对象类型为准,会调用谁的方法。
关于方法重写的一些特性:
为什么要重写方法?或者方法重写的目的是什么?
若子类从父类中继承过来的方法,不能满足子类特有的需求时,子类就需要重写父类中相应的方法,方法的重写也是程序扩展的体现
class Dog{ protected name; protected age; public void eat(){ System.out.println("开始干饭了") } } class HomeDog extends Dog{ public void eat(){ System.out.println("我爱吃米饭") } }
super可以完成一下的操作:
定义一个化妆品类(Cosmetic)
定义一个化妆品管理类(CosmeticManager)
实现进货功能
可以输出所有化妆品信息功能
使用继承实现一个可按单价排序输出所有化妆品的功能
使用继承实现一个只输出进口化妆品的功能
import java.util.Arrays; public class java12 { public static void main(String[] args) { ImportCosmeticManager cm = new ImportCosmeticManager(); cm.add(new Cosmetic("香奈儿","进口",1000)); cm.add(new Cosmetic("圣罗兰","进口",800)); cm.add(new Cosmetic("大宝","国产",20)); cm.add(new Cosmetic("万紫千红","国产",10)); cm.printAll(); } } class CosmeticManager{ protected Cosmetic[] cs = new Cosmetic[4]; protected int count=0; // 进货功能 public void add(Cosmetic c){ if(count>=cs.length){ int newLen = cs.length*2; cs = Arrays.copyOf(cs,newLen); } cs[count] = c; count++; } public void printAll(){ for (int i=0;i<cs.length;i++){ cs[i].getInfo(); } } } // 输出进口商品 class ImportCosmeticManager extends CosmeticManager{ public void printAll(){ for(int i=0;i<count-1;i++){ if("进口".equals(cs[i].getType())){ cs[i].getInfo(); } } } } //按照价格排序 class SortCosmeticManager extends CosmeticManager{ public void printAll(){ Cosmetic[] temp = Arrays.copyOf(cs,count); Cosmetic c = null; for(int i = 0;i<count-1;i++){ for(int j = 0;j<count-i-1;j++){ if(temp[j].getPrice()>temp[j+1].getPrice()){ c = temp[j]; temp[j] = temp[j+1]; temp[j+1] = c; } } } for(Cosmetic n:temp){ n.getInfo(); } } } // 化妆类 class Cosmetic{ private String name;// 品牌 private String type;// 进口或国产 private int price;// 价格 public Cosmetic(String name,String type,int price){ this.name = name; this.type = type; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public void getInfo(){ System.out.println("name="+getName()+",type="+getType()+",price="+getPrice()); } }
使用final关键字完成以下的操作:
使用final关键字声明一个常量
修饰属性或者修饰局部变量(最终变量),也称为常量。
使用final关键字声明一个方法
该方法为最终方法,且只能被子类继承,但不能被子类重写。
使用final关键字声明一个类
该类就转变为最终类,没有子类的类,final修饰的类无法被继承。
在方法参数中使用final,在该方法内部不能修改参数的值(在内部类中详解)
abstract class Animal{ public abstract void move() // 方法的声明,抽象方法只有声明,没有实现 } // 继承抽象类的具体类必须实现所有抽象方法 abstract class Person extends Animal{ public abstract void eat() } class Man extends Person{ public void move(){} public void eat(){} }
接口的定义格式
interface 接口名称{
全局常量;
抽象方法;
}
interface IEat{ // public abstract void eat(); //接口中只能定义抽象方法 void eat(); // 接口中定义的方法没有声明修饰符,默认为public abstract int Num = 10; // 常量 } interface IRun{ void run() } // 接口之间可以多继承(注意:类是只能单继承的) interface ISleep extends IEat,IRun{ void sleep(); } class Girl implements ISleep,IEat{ // 具体实现 }
面向对象设计原则:
多态是面向对象三大特征之一
什么是多态性 ?
对象在运行过程中的多种规范
多态性我们大概可以分为两类:
例如:
// 用父类的引用指向子类对象(用大的类型去接受小的类型,向上转型、自动转换)
Chicken home = new HomeChicken();
结论:
在编程时针对抽象类型的编写代码,成为面向对象编程(或面向接口编程)父类通常都定义为抽象类、接口
对象的多态性
对象多态性是从继承关系中的多个类而来
向上转型:将子类实例转为父类实例
格式:父类 父类对象 = 子类实例:=>自动转换
以基本数据类型操作为例: int i = ‘a’;
(因为char的容量比int小,所以可以自动完成)
**向下转型:**将父类实例转为子类实例
格式:子类 子类对象 = (子类)父类实例:强制转换
以基本数据类型操作为例: char c = (char)97;
因为整型是4个字街比char2个字节更大,所以需要强制完成
多态性小结
instanceof是用与检查是否为指定的类型,通常在把父类引用强制转换为子类引用时要使用,以避免发生类型转换异常
语法格式如下:
对象 instanceof 类型 --返回boolean类型值
示例:
if(homeChicken instanceof Chicken){}
该语句一般用于判断一个对象是否为某个类的实例,是返回true,否返回false
父类的设计法则
通过instanceof关键字,我们可以很方便的检查对象的类型,但如果一个父类的子类过多,这样的判断还是显得繁琐,那么如何去设计一个父类呢?
模板方法模式(Templete Method):定义一个操作中的算法的骨架,而将一些可变部分的实现延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定的步骤。
public class test12 { public static void main(String[] args) { UserManager um = new UserManager(); um.action("admin","del"); } } abstract class BaseManager{ public void action(String name,String method){ if("admin".equals(name)){ execute(method); }else{ System.out.println("你没有操作权限,请联系管理员"); } } public abstract void execute(String execute); } class UserManager extends BaseManager{ public void execute(String method){ if("add".equals(method)){ System.out.println("执行添加方法"); }else if("del".equals(method)){ System.out.println("执行删除方法"); } } }
策略模式(Strategy Pattern),定义了一系列的算法,将每一种算法封装起来并可以相互替换使用,策略模式让算法独立于使用它的客户应用而独立变换
OO设计原则:
import sun.awt.geom.AreaOp; //策略模式 public class test1 { public static void main(String[] args){ BaseSerive user = new UserSerive(); user.setIsSave(new NetSave()); user.add("用户"); } } interface IsSave{ public void save(String data); } class FileSave implements IsSave{ @Override public void save(String data) { System.out.println("把数据保存到文件中"+data); } } class NetSave implements IsSave{ @Override public void save(String data) { System.out.println("把数据保存到网络中"+data); } } abstract class BaseSerive{ private IsSave isSave; // 私有属性 public void setIsSave(IsSave isSave) { this.isSave = isSave; } public void add(String data) { System.out.println("检验数据合法性"); isSave.save(data); System.out.println("数据保存成功"); } } class UserSerive extends BaseSerive{ }
object类是类层次结构的根类
每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法
所有类都是Object类的子类。
返回该对象的字符串表示
通常,toString方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明且易于读懂的信息表达式。建议所有子类都重写此方法。
class Student{ private String name; private int sid; private int age; public Student(){} public Student(int sid,String name,int age){ this.name = name; this.sid = sid; this.age age; } // 重写Object类中的toString方法 public String toString(){ return "sid="+sid+",name="+name+",age="+age; } }
指示其他某个对象是否与此对象“相等”,equals方法在非空对象引用上实现相等关系:
自反性 对称性 传递性 一致性
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写finalize方法,以配置系统资源或执行其他清除
返回次Object的运行时类
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单使用的模式。
// 使用工厂来降低两者之间的依赖 // 使用者和被使用者两者之间,耦合,产生了依赖,当被使用者改变时,会影响使用者 class ProductFactory{ public static Product getProduct(String name){ if("phone".equals(name)){ return new Phone(); }else if("computer".equals(name)){ return new Computer; }else{ return null } } } interface Product{ public void work(); } class Phone implements Product{ public void work(){ System.out.println("手机开始工作。。。") } } class Computer implements Product{ public void work(){ System.out.println("电脑开始工作。。。") } }
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问
代理模式说白了就是”真实对象“的代表,在访问对象时引用一定程度的间接性,因为这种间接性可以附加多种用途。
public class test13 { public static void main(String[] args) { UserAction userAction = new UserAction(); ActionProxy proxy = new ActionProxy(userAction); proxy.doAction(); } } interface Action{ public void doAction(); } class ActionProxy implements Action{ private Action target; public ActionProxy(Action target){ this.target = target; } @Override public void doAction() { long startTime = System.currentTimeMillis(); target.doAction(); long endTime = System.currentTimeMillis(); System.out.println("共耗时"+(endTime-startTime)); } } class UserAction implements Action{ @Override public void doAction() { for(int i = 0;i < 100;i++){ System.out.println("用户开始工作..."); } } }
适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
oo设计原则:
public class test14 { public static void main(String[] args) { PowerA powerA = new PowerAImp1(); PowerB powerB = new PowerBImp1(); Adapter adapter = new Adapter(powerB); work(adapter); } public static void work(PowerA a){ System.out.println("正在连接。。。"); a.insert(); System.out.println("工作结束。。。"); } } class Adapter implements PowerA{ private PowerB powerB; public Adapter(PowerB powerB){ this.powerB = powerB; } @Override public void insert() { powerB.connect(); } } interface PowerA{ public void insert(); } class PowerAImp1 implements PowerA{ @Override public void insert() { System.out.println("电源A开始工作"); } } interface PowerB{ public void connect(); } class PowerBImp1 implements PowerB{ @Override public void connect() { System.out.println("电源B开始工作"); } }
内部类就是在一个类的内部定义的类
格式如下:
class Outer{
class Inner{}
}
编译上诉代码会产生两个文件:
Outer.class和Outer$Inner.class
内部类可以作为一个类的成员外,还可以把类放在方法内定义
注意:
在一个类内部定义一个静态内部类
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。静态嵌套类仅能访问外部类的静态成员和方法
匿名内部类就是没有名字的内部类。
匿名内部类的三种情况:
在使用匿名内部类时,要记住一下几个原则:
public class test15 { public static void main(String[] args) { Outer outer = new Outer(); outer.innerPrint(); outer.show(); Outer.Inner3 inner3 = new Outer.Inner3(); inner3.print(); outer.print1(); outer.print2(); outer.print3(new Eat() { @Override public void eat() { System.out.println("eat:参数式匿名内部类"); } }); // Outer.Inner inner = outer.new Inner(); // inner.print(); } } class Outer{ private String name; public void innerPrint(){ Inner inner = new Inner(); inner.print(); } // 成员内部类 private class Inner{ public void print(){ System.out.println("成员内部类"); } } // 方法内部类 public void show(){ class Inner2{ public void print(){ System.out.println("方法内部类"); } } Inner2 inner2 = new Inner2(); inner2.print(); } // 静态内部类 static class Inner3{ public void print(){ System.out.println("静态内部类"); } } // 匿名内部类 public void print1(){ Cat cat = new Cat(){ @Override public void eat() { System.out.println("eat:继承式匿名内部类"); } }; cat.eat(); } public void print2(){ Eat eat = () -> System.out.println("eat:接口式匿名内部类"); eat.eat(); } public void print3(Eat eat){ eat.eat(); } } abstract class Cat{ public abstract void eat(); } interface Eat{ void eat(); }
问题:局部内部类访问局部变量必须用final修饰,为什么?
当调用这个方法时,局部变量如果没有final修饰,它的生命周期和方法的生命周期是一样的,当方法被调用时会入栈,方法结束后即弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,显然已经没法使用了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了
c,protected,private,static
4. 一定是在new的后面,用其隐含实现一个接口或实现一个类
5. 匿名内部类为局部的,所以局部内部类的所有限制都对其生效
public class test15 { public static void main(String[] args) { Outer outer = new Outer(); outer.innerPrint(); outer.show(); Outer.Inner3 inner3 = new Outer.Inner3(); inner3.print(); outer.print1(); outer.print2(); outer.print3(new Eat() { @Override public void eat() { System.out.println("eat:参数式匿名内部类"); } }); // Outer.Inner inner = outer.new Inner(); // inner.print(); } } class Outer{ private String name; public void innerPrint(){ Inner inner = new Inner(); inner.print(); } // 成员内部类 private class Inner{ public void print(){ System.out.println("成员内部类"); } } // 方法内部类 public void show(){ class Inner2{ public void print(){ System.out.println("方法内部类"); } } Inner2 inner2 = new Inner2(); inner2.print(); } // 静态内部类 static class Inner3{ public void print(){ System.out.println("静态内部类"); } } // 匿名内部类 public void print1(){ Cat cat = new Cat(){ @Override public void eat() { System.out.println("eat:继承式匿名内部类"); } }; cat.eat(); } public void print2(){ Eat eat = () -> System.out.println("eat:接口式匿名内部类"); eat.eat(); } public void print3(Eat eat){ eat.eat(); } } abstract class Cat{ public abstract void eat(); } interface Eat{ void eat(); }
问题:局部内部类访问局部变量必须用final修饰,为什么?
当调用这个方法时,局部变量如果没有final修饰,它的生命周期和方法的生命周期是一样的,当方法被调用时会入栈,方法结束后即弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,显然已经没法使用了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了