①友好变量、友好方法、友好类
不用private、public、protected修饰符修饰的成员变量和方法被称为友好变量和友好方法。
例:当在另一个类中用类Tom创建了一个对象后,如果这个类与Tom类在同一个包中,那么该对象能访问自己的友好变量和友好方法。
②实例变量与类变量
声明
class A{
float x;//实例变量
static int y;//类变量
}
区别:1.不同对象的实例变量互不相同
2.所有对象共享类变量
3.通过类名直接访问类变量
③实例方法与类方法
声明
class A{
int a;
float max(float x,float y){....}//实例方法
static float jerry(){....}//类方法
}
区别:1.对象调用实例方法
2.类名调用类方法
注意:若一个类中要使用友好类创建对象,亚保证他们在同一个包中。
1.不能用protected和private修饰类
2.访问限制修饰符按访问权限从高到低的排列顺序是public、
protected、友好的、private
④数据特点
栈特点:
1.栈描述的方法执行的内存模型,每个方法都被调用都会创建一个栈帧 (存储局部变量、操作数、方法出口等)
2.JVM为每个线程创建一个栈、用于存放该线程执行方法的信息(实际参数、局部变量等)
3.栈属于线程所有,不能实现线程间的共享
4.栈的存储特性是“先进后出,后进先出”
5.栈是由系统自动分配,速度快!栈是一个连续的存储空间
堆特点:
1.堆用于存储创建好的对象和数组(数组也是对象)
2.JVM只有一个堆,被所有线程共享
3.堆是一个不连续的内存空间,分配灵活,速度慢
方法区(静态区)特点:
1.JVM只有一个方法区,被所有线程共享
2.方法区实际也有堆,只适用于存储类、常量相关信息
3.用来存放程序中永远是不变或唯一的内容
⑤使用标准类
1.使用java.util.Scanner
import java.util.Scanner;//可以不用每次都输入java.util.Scanner
Scanner对每个基本类型,都会有对应的nextxxx()方法,
如nextByte(),nextShort(),nextLong(),nextFloat(),nextDouble(),nextBoolean()等,
如果直接取得上一个字符串(以空格或换行分割),则用next(),如果想取得用户输入的整行文字,则使用nextLine()
2.使用java.math.BigDecimal
由于浮点数的运算是不精确的,如果要求精确度,那就要小心使用浮点数,
而且别用==直接比较浮点数运算结果
import java.math.BigDecimal;
在类里写
BigDecimal operand1=new BigDecimal("1.0");
BigDecimal operand2=new BigDecimal("0.8");
BigDecimal result=operand1.subtract(operand2);
//相减,BigDecimal提供有plus()、substract()、multiply()、
//divide()等方法,可以进行加减乘除等运算
此时得到的结果是精确的。
3.对象指定与相等性
在上一范例中,比较两个BigDecimal是否相等,是使用equals()方法而非使用==运算。原因如下:
如果你在操作对象,=是用在指定参考名称参考某个对象,而==是用在比较两个参考名称是否参考同一对象。
例:
BigDeciaml a=new BigDecimal("0.1");
BigDeciaml b=new BigDeciaml("0.1");
System.out.println(a==b);//显示false
System.out.println(a.equal(b));//显示true
⑥基本类型打包器
1.打包基本类型
可以使用Long、Integer、Double、Float、Boolean、Byte等类来打包基本类型,正如此名称所示,这些类主要目的就是提供对象实例作为“壳”,将基本类型打包在对象之中,这样就可以操作这些对象,就像是基本类型当做对象操作。
例:
int data1=10;
int data2=20;
Integer wrapper1=new Integer(data1);//打包基本类型
Integer wrapper2=new Integer(data2);
System.out.println(data1/3);//基本类型运算 输出3
System.out.println(wrapper1.doubleValue()/3);
//操作打包器方法 输出3.33333333
System.out.println(wrapper1.compareTo(wrapper2));
基本类型打包器都是归于java.lang包中,如果要使用Integer打包int类型数据,方法之一是使用new创建Integer实例时,传入int型数据。因为如果表达式中都是int,就只会在int控件中做运算,结果会是int整数,因此data1/3就会显示3的结果,也可以操作Integer的doubleValue()将打包值以都变了类型返回,这样就会在double空间中做相除,结果就是3.333333…
Integer提供compareTo()方法,可与另一个Integer对象进行比较,如果打包值相同就返回0,小于compareTo()传入对象打包值就返回-1.否则就是1.
2.自动装箱、拆箱
除了使用new创建基本类型打包器之外,可以这样打包基本类型:
Integer wrapper=10;
编译程序会自动判断是否能进行自动装箱,用自动装箱改写一下上面的代码
Integer data1=10;
Integer data2=20;
System.out.println(data1.doubleValue()/3);
System.out.println(data1.compareTo(data2));
程序看来简洁很多
还可以按如下形式写
int i=10;
Integer wrapper=i;
也可以使用更一般化的Number类来自动装箱。例如:
Number number=3.14f;
3.14f会先被自动装箱为Float,然后指定给number。
可以自动装箱,也可以自动拆箱。
例如:
Integer wrapper=10;//自动装箱
Integer foo=wrapper;//自动拆箱
wrapper会参考至Integer,若被指定给int型的变量foo,则会自动取得打包的int类型再指定给foo
运算时,也可以进行自动装箱与拆箱
例如:
Integer i=10;
System.out.println(i+10);
System.out.println(i++);
上例中会显示20,10,编译程序会自动装箱与自动拆箱,也就是10会先装箱,然后在i+10时会先对i拆箱,再进行加法运算,i++该行也是先对i拆箱再进行递增运算。
3.自动装箱、拆箱内幕
使用Integer.valueOf()也是为基本类型建立打包器的方式之一
Integer i=null;
int j=i;
但执行时会有错误,因为编译程序会将之展开为:
Object localObject=null;
int i=localObject.inValue();
在Java程序代码中,null代表一个特殊对象,任何类声明的参考名称都可以参考至null,表示该名称没有参考任何对象实体。
但是,有一些隐藏的细节需要注意,例如:
Integer i1=100;
Integer i2=100;
if(i1=i2){
System.out.println("i1==i2");
}
else{
System.out.println("i1!=i2");
}//输出i1==i2
如果将100换为200,则情况不同
Integer i1=200;
Integer i2=200;
if(i1=i2){
System.out.println("i1==i2");
}
else{
System.out.println("i1!=i2");
}//输出i1!=i2
原因是valueOf()的操作内容:
public static Integer valueOf(int i){
if(i>=IntegerCache.low && i<=IntegerCache.high)
return IntegerCache.cache[i+(-IntegerCache.low)];
}
//其中IntegerCache.low默认值为-128,IntegerCache.high默认值是127
//所以出现了上面的情况,如果想要修改默认值,可以
//在启动JVM时,使用系统属性java.lang.IntegerCache.high来指定。例如:
//java -Djava.lang.IntegerCache.high=300 cc.openhome.Demo
//cc.openhome.Demo是文件名
⑦数组对象
1.如果要存储10个变量
int score1=88;
//...
int score10=93;
实际上不可能这么做,用数组处理即可
int[] scores={88,81,74,68,78,76,77,85,95,93};
如果想一次取出每一个值,方法之一是使用for循环
int[] scores={88,81,74,68,78,76,77,85,95,93};
for(int i=0;i<scores.length;i++){
System.out.printf("学生分数:%d %n",scores[i]);
}
也可以写成:
for(int score:scores){
System.out.printf("学生分数:%d %n",score);
}
2.二维数组
int[][] cords={
{1,2,3},
{4,5,6}
}; //声明二维数组并赋予初始值
for(int x=0;x<cords.length;x++){ //得知有几列
for(int y=0;y<cords[x].length;y++){ //取得每列的长度
System.out.printf("%2d",cords[x][y]); //指定列、行索引取得数组元素
}
System.out.println();
}
用增强式for语句写起来比较简练:
for(int[] row:cords){
for(int value:row){
System.out.printf("%2d",value);
}
System.out.println();
}
3.操作数组对象
用new关键字建立数组:
int[] scores=new int[10];
int类型定义的数组,初始化默认是0
String类型定义的数组,默认值是null
char类型定义的数组,默认值是0对应的字符
double类型定义的数组,默认值是0.0
float类型定义的数组,默认值是0.0
如果默认初始值不符合要求,可以使用java.util.Arrays的fill()方法来设定新建数组的元素值。
例如:将每个学生的成绩默认为60分起:
import java.util.Arrays;//引入方法
int[] scores=new int[10];
for(int score:scores){
System.out.printf("%2d",score);//输出0 0 0 0 0 0 0 0 0 0
}
System.out.println();
Arrays.fill(scores,60);
for(int score:scores){
System.out.printf("%3d",score);//输出60 60 60 60 60 60 60 60 60 60
}
如果想在new数组中一并指定初始值,可以这样写,注意不必指定数组长度:
int[] scores=new int[]{88,81,74,68,78,76,77,85,95,93};
看看如下例子:
int[] score1={88,81,74,68,78,76,77,85,95,93};
int[] score2=score1;
score2[0]=99;
System.out.println(score1[0]);//输出99
因为数组是对象,而score1与score2是参考名称,将score1指定给score2,意思就是将score1参考的对象也给score2参考
如果想用new建立二维数组,也可以如下:
int[][] cords=new int[2][3];
没有人规定二维数组一定得是矩阵,也可以建立不规则的数组,例如:
int[][] arr=new int[2][];//声明arr参考的对象会有两个索引
arr[0]=new int[]{1,2,3,4,5};//arr[0]是长度为5的一维数组
arr[1]=new int[]{1,2,3};//arr[1]是长度为3的一维数组
所以,这么建立数组也是可以的:
int[][] arr={
{1,2,3,4,5},
{1,2,3}
};
关于为什么int的默认值为0,而Integer的默认值为null:
Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。
int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况
4.数组复制
了解完数组是对象,我们就应该知道,以下这个并非数组复制:
int[] scores1={88,81,74,68,78,76,77,85,95,93};
int[] scores2=scores1;
这个程序段不过是将scores1参考的数组对象,也是给scores2参考,如果真的要数组复制,基本做法是另行建立数组。
例如:
int[] scores1={88,81,74,68,78,76,77,85,95,93};
int[] scores2=new int[scores1.length];
for(int 1=0;i<scores1.length;i++){
scores2[i]=scores1[i];
}
事实上,可以使用System.arraycopy()方法,这个方法会使用原生方式复制每个索引元素,比自行使用循环来得快
int[] scores1={88,81,74,68,78,76,77,85,95,93};
int[] scores2=new int[scores1.length];
System.arraycopy(scores1,0,scores2,0,score1.length);
//System.arraycopy()的五个参数分别是来源数组、来源起始索引、目的数组、目的起始索引、复制长度
JDK6以上,还可以使用更方便的Arrays.copyOf()方法,你不用另行建立新数组,Arrays.copyOf()会帮我们建立
例如:
import java.util.Arrays;
int[] scores1={88,81,74,68,78,76,77,85,95,93};
int[] scores2=Arrays.copyOf(score1,score1.length);
//copyOf(复制数组名,长度)
//如果修改score2的内容,不影响score1
以上都是基本类型数组,对于类类型声明的数组则要注意参考的行为。
例:
class Clothes{
String color;
char size;
Clothes(String color,char size){
this.color=clor;
this.size=size;
}
}
public class ShallowCopy{
public static void main(String[] args){
Clothes[] c1={new Clothes("red","L"),new Clothes("blue","M")};
Clothes[] c2=new Clothes[c1.length];//浅层复制
for(int i=0;i<c1.length;i++){
c2[i]=c1[i];
}
c1[0].color="yellow";//通过c1修改索引0对象
System.out.println(c2[0].color);//通过c2取得索引0对象的颜色 输出yellow
}
}
实际上循环中仅将c1每个索引处所参考的对象,也给c2每个索引来参考,并没有实际复制出Clothes对象,术语上来讲,这叫做复制参考,或者称这个行为是浅层复制,无论System.arraycopy()还是Array.copyOf(),用在类类型声明的数组时,都是执行浅层复制。
如果真想要连同对象一同复制,我们需要自行操作,因为基本上只有自己才知道每个对象复制时,有哪些属性必须复制,例如:
class Clothes2{
String color;
char size;
Clothes(String color,char size){
this.color=clor;
this.size=size;
}
}
public class DeepCopy{
public static void main(String[] args){
Clothes2[] c1={new Clothes("red","L"),new Clothes("blue","M")};
Clothes[] c2=new Clothes[c1.length];
for(int i=0;i<c1.length;i++){
Clothes2 c=new Clothes2(c1[i].color,c1[i].size);
c2[i]=c;
}
c1[0].color="yellow";
System.out.println(c2[0].color);//输出red
}
}
5.字符串对象
字符串基础
.length()//取得字符串长度
.charAt()//指定取得字符串中某个字符,索引从0开始
.toUpperCase()//将原本小写的字符串内容转为大写的字符串内容
static byte parseByte(String str)//:将字符串参数解析为一个有符号的十进制byte
static short parseShort(String str)//:将字符串参数解析为一个有符号的十进制short
static int parseInt(String str)//:将字符串参数解析为一个有符号的十进制int
static long parseLong(String str)//:将字符串解析为一个有符号的十进制long
static float parseFloat(String str)//:将字符串参数解析为float值
static double parseDouble(String str)//:将字符串参数解析为double值
static boolean parseBoolean(String str)//:将字符串参数解析为boolean值
例:
long number=0;
number=Long.parseLong("1");//此时number=1
字符串特性
看看下面的程序片段,是返回true还是false
char[] name=('J','u','s','t','i','n');
String name1=new String(name);
String name2=new String(name);
System.out.println(name1==name2);//false
由于name1,name2分别参考至创建出来的String对象
那么下面的代码呢?
String name1="justin";
String name2="justin";
System.out.println(name1==name2);//true
由于name1与name2参考同一个对象,Java为了效率考虑,以""包括的字符串,只要内容相同(序列、大小写相同),无论在程序代码中出现几次,JVM都只会建立一个String实例,并在字符串池中维护。
那么下面的代码呢?
String name1="justin";
String name2="justin";
String name3=new String("justin");
String name4=new String("justin");
System.out.println(name1==name2);//true
System.out.println(name1==name3);//false
System.out.println(name3==name4);//false
因为justin会建立String实例并在字符串池中维护,所以name1与name2参考的是同一个独享,而new一定是建立新的对象,所以name3与name4分别参考至新建的String实例。
如果想比较字符串实际字符内容是否相同,不要使用==,要使用equals()。
String name1="justin";
String name2="justin";
String name3=new String("justin");
String name4=new String("justin");
System.out.println(name1.equal(name2));//true
System.out.println(name1.equal(name3));//true
System.out.println(name3.equal(name4));//true
不可变动字符串
String name1="java";
String name2=name1+"world";
System.out.println(name2);
反编译上面的代码
String s="java";
String s1=(new StringBuilder()).append(s).append("world").toString();
System.out.println(s1);
来看看这个:
String text1="Ja"+"va";
String text2="java";
System.out.println(text1==text2);//true
反编译:
String s="Java";
String s1="java";
System.out.println(s==s1);
这就是为什么输出true
对象数组
public class CashCard {//现金卡类
protected String id;//代号
protected double money;//资金
public CashCard() {}//无参构造函数
public CashCard(String id, double money) {//有参构造函数
this.id = id;
this.money = money;
}
public String getId() {//得到代号
return id;
}
public void setId(String id) {//设置代号
this.id = id;
}
public double getMoney() {//查到资金数
return money;
}
public void addMoney(double money) {//增加资金数
if(money>0) {
this.money += money;
}
else {//如果钱数输入错误
System.out.println("money you add is wrong");
}
}
public void drawMoney(double money) {//取钱
if(money<this.money&&money>=0) {//当钱数大于等于0,且取钱数小于资金数时
this.money-=money;
}
else if(money<0) {//当取钱数小于0时
System.out.println("money you draw is not right!!!");
}
else {//当取钱数大于资金数时
System.out.println("Your money is not enough!!!");
}
}
}
public class IrateCashCard extends CashCard{
protected double irate;//利率
IrateCashCard(){}//无参构造函数
IrateCashCard(String id,double money){//有参构造函数
super(id,money);//继承父类构造方法
}
public double getIrate(){//通过存取的钱数,给定利率
if(money>0&&money<=1000) {
this.irate=0.01;
}
else if(money>1000&&money<10000) {
this.irate=0.02;
}
else if(money>10000&&money<100000) {
this.irate=0.04;
}
else if(money>100000&&money<1000000) {
this.irate=0.05;
}
else if(money>=1000000){
this.irate=0.06;
}
else {
System.out.println("your money you put is wrong!");
}
return irate;
}
public static void main(String[] args) {
IrateCashCard[] cards = { //创建数组对象
new IrateCashCard("A001", 1000),
new IrateCashCard("A002", 1500),
new IrateCashCard("A003", 2000),
new IrateCashCard("A004", 2500),
new IrateCashCard("A005", 1000000) };
cards[0].addMoney(10000);//存10000
cards[1].drawMoney(200);//取200
for (IrateCashCard card : cards) {//输出当前5张卡的id与资金数以及利率
System.out.println(card.id+" "+card.money+" "+card.getIrate());
}
}
}
/*输出:
A001 11000.0 0.04
A002 1300.0 0.02
A003 2000.0 0.02
A004 2500.0 0.02
A005 1000000.0 0.06*/
可以改进程序 存钱函数addMoney()可以这样写
Scanner scanner=new Scanner(System.in);
cards[1].addMoney(scanner.nextDouble());
这样用户可以键入钱数。