首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

MongoDB学习:备份恢复(一)

本文主要介绍MongoDB副本集备份恢复的方法、常用命令,以及一些具体的操作过程,最后介绍一种备份方案。 一、备份方法 0、oplog 0.1oplog是什么oplog是一个 capped collection(有上限的集合),位于local库里 0.2local库local库是MongoDB的系统库,记录着时间戳和索引和复制集等信息0.2.1没有建立副本集的时候 > show collections startup_log 0.2.2建立的副本集之后(3.4、3.6和4.0都是这些集合) replSet27017:PRIMARY> use local switched to db local replSet27017:PRIMARY> show collections me #me集合保存了服务器名称 oplog.rs #oplog.rs集合记录对节点的所有操作 replset.election replset.minvalid #replset.minvalid集合保存了数据库最新操作的时间戳 replset.oplogTruncateAfterPoint startup_log #startup_log集合记录这mongod每一次的启动信息 system.replset #system.replset记录着复制集的成员配置信息,rs.conf()读取这个集合 system.rollback.id 0.3查看oplog0.3.1oplog字段说明 replSet27017:PRIMARY> db.oplog.rs.find().pretty().limit(1) { "ts" : Timestamp(1558940829, 599), "t" : NumberLong(12), "h" : NumberLong("6800425020320237930"), "v" : 2, "op" : "i", "ns" : "config.system.sessions", "ui" : UUID("10b4d6ac-37df-4586-8a2d-83a3389d7406"), "wall" : ISODate("2019-05-16T08:44:39.629Z"), "o" : { "_id" : { "id" : UUID("fa4f2ff2-8251-4dec-b517-ebcab6db2eca"), "uid" : BinData(0,"LkqZkoMQbGtpaIwmLLkI3wtgnsDvRhwXsdiic1EAMLE=") }, "lastUse" : ISODate("2019-05-16T08:44:39.629Z"), "user" : { "name" : "test@test01" } } ts: 操作时间,8字节的时间戳,由4字节unix timestamp + 4字节自增计数表示(表示某一秒内的第几次操作) h:操作的全局唯一标识 v:oplog版本信息 op:1字节的操作类型 i:插入操作 u:更新操作 d:删除操作 c:执行命令(如createDatabase,dropDatabase) n:空操作,会定期执行以确保时效性 ns:操作针对的集合 o:操作内容 o2:在执行更新操作时的where条件,仅限于update时才有该属性 #查看oplog的信息 replSet27017:PRIMARY> db.printReplicationInfo() configured oplog size:1024MB log length start to end: 1866871secs (518.58hrs) oplog first event time:Tue May 14 2019 23:15:10 GMT+0800 (CST) oplog last event time:Wed Jun 05 2019 13:49:41 GMT+0800 (CST) now:Mon May 20 2019 03:20:48 GMT+0800 (CST) #查看slave的同步状态 replSet27017:PRIMARY> db.printSlaveReplicationInfo() source: 192.168.1.12:27017 syncedTo: Wed Jun 05 2019 13:49:41 GMT+0800 (CST) 0 secs (0 hrs) behind the primary source: 192.168.1.13:27017 syncedTo: Wed Jun 05 2019 13:49:41 GMT+0800 (CST) 0 secs (0 hrs) behind the primary 0.3.2根据操作类型查操作 查询oplog里的insert记录,对应op为i的记录: replSet27017:PRIMARY> db.oplog.rs.find({"op" : "i"}).pretty().limit(3) 查update操作命令: replSet27017:PRIMARY> db.oplog.rs.find({"op" : "u"}).pretty().limit(3) test:PRIMARY> 查delete操作命令: replSet27017:PRIMARY> db.oplog.rs.find({"op" : "d"}).pretty().limit(3) 0.3.3根据时间查询操作 mongodb里的时间 #操作系统时间 #date Mon May 20 02:33:45 CST 2019 #mongo里显示当地时间 replSet27017:PRIMARY> Date() Mon May 20 2019 02:34:21 GMT+0800 (CST) #构建一个格林尼治时间,我们是东八区,所以需要减去8个小时 replSet27017:PRIMARY> new Date ISODate("2019-05-19T18:34:24.157Z") #也是格林尼治时间 replSet27017:PRIMARY> ISODate() ISODate("2019-05-19T18:34:28.820Z") #时间戳 ISODate().valueOf() 1558291032161 1、复制文件 1.1 文件系统快照要创建一致的备份,必须锁定数据库,并且必须在备份过程中暂停对数据库的所有写入;快照反映了所有的数据和journal log开始的时刻,没有增量备份。1.1.1 使用LVM创建快照 lvcreate --size 100M --snapshot --name mdb-snap01 /dev/vg0/mdb-snap01 --snapshot 创建一个LVM快照--size 快照的上限大小,如果耗尽空间,快照将无法使用 1.1.2 归档快照如将上述快照挂载后压缩 umount /dev/vg0/mdb-snap01 dd if=/dev/vg0/mdb-snap01 | gzip > mdb-snap01.gz 1.1.3 恢复快照 lvcreate --size 1G --name mdb-new vg0 gzip -d -c mdb-snap01.gz | dd of=/dev/vg0/mdb-new mount /dev/vg0/mdb-new /srv/mongodb 将mdb-snap01.gz 解压并放到mdb-new里将mdb-new挂载到 /srv/mongodb目录, 根据需要修改挂载点以对应MongoDB数据文件位置 注意: 还原的快照将具有mongod.lock文件。如果不从快照中删除此文件,MongoDB可能会认为是正常关闭。 如果在启用storage.journal.enabled的情况下运行,并且不使用db.fsyncLock(),则无需删除mongod.lock文件;如果使用db.fsyncLock(),则需要删除mongod.lock文件。 1.1.4 将备份放到远程存储过程与上述相同,只是使用SSH在远程系统上归档和压缩备份。 umount /dev/vg0/mdb-snap01 dd if=/dev/vg0/mdb-snap01 | ssh username@example.com gzip > /opt/backup/mdb-snap01.gz lvcreate --size 1G --name mdb-new vg0 ssh username@example.com gzip -d -c /opt/backup/mdb-snap01.gz | dd of=/dev/vg0/mdb-new mount /dev/vg0/mdb-new /srv/mongodb 1.1.5 大体过程 锁定数据库,使用db.fsyncLock() 执行上述快照备份命令 快照完成,解锁数据库,使用db.fsyncUnlock() 1.2 直接复制文件相比于文件系统快照,直接复制文件更为简单1.2.1 复制数据文件直接cp数据文件无法复制所有的文件,所以需要在备份的时候防止数据文件发生改变,做法也使用fsynclock命令。 replSet27017:PRIMARY> db.fsyncLock() 该命令会锁定(lock)数据库,禁止任何写入,并进行同步(fsync),即将所有的脏页刷到磁盘。Mongod会将之后的所有写操作加入等待队列 cp -R/data1/mongodb27017/* /backup/mongodb27017_20190704 数据复制完成后,解锁数据库: replSet27017:PRIMARY> db.fsyncUnlock(); 注意:如果启用了身份验证,则在调用fsyncLock()和fsynUnlock()期间不要关闭shell。如果断开了连接,则可能无法进行重新连接,并不得不重启mongod。FsyncLok()的设定是在重启后不会保持生效,mongod总是以非锁定模式启动的。 当然,除了使用fsyncLock()外,关闭mongod,复制文件,再启动mongod,也是一种方法。 2、mongodump 与 mongorestore 2.1 mongodump2.1.1 常用参数 -d:数据库名 -c:集合名 -o:要导出的文件名 -q:导出数据的过滤条件 --authenticationDatabase:保存用户凭证的数据库 --gzip:备份时压缩 --oplog:导出的同时生成一个oplog.bson文件,存放在你开始进行dump到dump结束之间所有的oplog 2.1.2常用备份操作 #备份所有库 ./mongodump -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -o /data/mongodb/backup/full_20190525 #备份test01库 ./mongodump -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d test01 -o /data/mongodb/backup/20190525 #备份test01库下 testCollection集合 ./mongodump -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d test01 -c testCollection -o /data/mongodb/backup/20190525 2.2 mongorestore2.2.1 常用参数 --drop: 恢复每个集合之前先删除 --oplogReplay: 重放oplog.bson中的操作内容 --oplogLimit: 与--oplogReplay一起使用时,可以限制重放到的时间点 2.3 模拟不断插入操作过程中备份和恢复(恢复到单节点)模拟不断插入集合test.table use test1 for(var i = 0; i < 10000; i++) { db.test.table.insert({a: i}); } 在插入过程中mongodump备份并指定--oplog: ./mongodump -vv -h127.0.0.1:27017 -uroot -prootroot --authenticationDatabase admin --oplog -o /data/mongodb/backup 恢复test1库(在一个单节点恢复) ./mongorestore -h127.0.0.1:27018 -d test1 /data/backup1/test1 查看插入了多少条 > db.test.table.count() 4029 应用插入期间记录的oplogmongorestore -vv -h127.0.0.1:27018 --oplogReplay /data/backup1查看应用了oplog后插入了多少条(说明这些是在备份期间插入的) > db.test.table.count() 4044 注意:--oplog选项只对全库导出有效,所以不能指定-d选项 查看oplog.bson内容 ./bsondump /data/mongodb/backup1/oplog.bson 3、mongoexport 与 mongoimport 3.1 mongoexport参数: -csv:指明要导出为csv格式 -f:指明需要导的字段 备份集合collection1 ./mongoexport -h 192.168.1.12:27017 -uroot -prootroot --authenticationDatabase admin -d db01 -c collection1 -o /data/mongodb/backup1/collection1.json 导出的数据 {"_id":{"$oid":"5cf8c674bd10185cb185067f"},"name":"hushi","num":0.0} ... 导出为csv格式 ./mongoexport -h 192.168.1.12:27017 -uroot -prootroot --authenticationDatabase admin -d db01 -c collection1 --type=csv -f _id,name,num -o /data/mongodb/backup1/collection1.csv 导出的数据 _id,name,num ObjectId(5cf8c674bd10185cb185067f),hushi,0 ObjectId(5cf8c674bd10185cb185068b),hushi,12 ... 3.2 mongoimport参数: --headerline: 指明第一行是列名,不需要导入 -csv:指明要导入的是csv格式 -f:指明需要导入的字段 恢复之前备份的集合 mongoimport -h192.168.1.14:27018 -d test -c collection1 /data/collection1.json 恢复之前备份的集合 mongoimport -h 127.0.0.1:27018 -d test1 -c collection1 --type=csv --headerline --file /data/collection1.csv 4、Percona Server for MongoDB 4.1 热备原理可以想象成xtrabackup工具 备份: 首先会启动一个后台检测的进程,实时检测MongoDB Oplog的变化,一旦发现oplog有新的日志写入,立刻将日志写入到日志文件WiredTiger.backup中(你可以strings WiredTiger.backup查看oplog操作日志的变化); 复制MongoDB dbpath的数据文件和索引文件到指定的备份目录里; 恢复: 将WiredTiger.backup日志进行回放,将操作日志变更应用到WiredTiger引擎里,最终得到一致性快照恢复;把备份目录里的数据文件直接拷贝到你的dbpath下,然后启动MongoDB即可,会自动接入副本集集群。 4.2 下载安装4.2.1 下载热备由percon的mongodb版本实现,所以可以直接用percona版的mongodb,也可以将percona mongo当做一个副本集的二级节点。https://www.percona.com/downloads/percona-server-mongodb-3.6/LATEST/4.2.2 安装启动因为是要加入副本集,所以修改配置文件并启动: cp mongodb27017/mg27017.conf mongodb27018/mg27018.conf cp ../mongodb27017/mongo.keyfile . ./mongod -f /data1/mongodb27018/mg27018.conf 4.2.3 加入副本集percona 从节点: 在主节点上执行: replSet27017:PRIMARY> rs.add({host: "192.168.1.12:27018", priority: 1, hidden: false}) percona 从变为: > replSet27017:OTHER> 执行slaveOk(),为进行过验证 replSet27017:OTHER> rs.slaveOk() replSet27017:SECONDARY> 4.3 备份从节点进行备份: replSet27017:SECONDARY> use admin switched to db admin replSet27017:SECONDARY> db.runCommand({createBackup: 1, backupDir: "/backup/mongodb_20180704"}) { "ok" : 1, ... 开始备份后,立即开始往一个新集合里写数据: replSet27017:PRIMARY> for (i = 0; i < 10000; i++){ db.test6.save({'name':'hushi','num':i,date:new Date()}); } 备份完成后的数据目录:[root@vm4 /backup/mongodb_20180722]#ll -httotal 240K drwx------ 2 root root 221 Jul 22 14:43 test1 drwx------ 2 root root 4.0K Jul 22 14:43 test01 drwx------ 2 root root 4.0K Jul 22 14:43 test drwx------ 2 root root 4.0K Jul 22 14:43 mms_test drwx------ 2 root root 4.0K Jul 22 14:43 local drwx------ 2 root root 38 Jul 22 14:43 journal drwx------ 2 root root 301 Jul 22 14:43 db01 drwx------ 2 root root 216 Jul 22 14:43 config drwx------ 2 root root 4.0K Jul 22 14:43 admin -rw------- 1 root root 44K Jul 22 14:43 sizeStorer.wt -rw------- 1 root root 114 Jul 22 14:43 storage.bson -rw------- 1 root root 44K Jul 22 14:43 _mdb_catalog.wt -rw------- 1 root root 123K Jul 22 14:43 WiredTiger.backup -rw------- 1 root root 45 Jul 22 14:43 WiredTiger 4.4 恢复恢复都单节点,所以去掉配置文件里面的副本集信息传输rsync -r /backup/mongodb_20180704 192.168.1.13:/backup/ 将文件复制到配置文件目录下cp -R /backup/mongodb_20180704/* /data1/mongodb27018 将keyFile和配置文件也复制过来(也可以直接去掉验证,将security相关的参数都注释)修改配置文件后,启动./mongod -f /data1/mongodb27018/mg27018.conf 发现最后一条数据的日期晚于开始备份日期,所以验证了恢复后会直接进行日志重放 5、MongoDB Cloud Manager 5.1 介绍MongoDB Cloud Manager是官方推出的运维自动化管理系统,是企业版才支持的功能,社区用户也可以下载试用。Cloud Manager 主要功能包括 MongoDB 集群(复制集、分片)的自动化部署 集群监控、及报警定制 自动数据备份与还原 5.2 部署MongoDB Cloud Manager5.2.1 下载agent和安装 curl -OL https://cloud.mongodb.com/download/agent/automation/mongodb-mms-automation-agent-10.2.0.5851-1.rhel7_x86_64.tar.gz tar -xvf mongodb-mms-automation-agent-10.2.0.5851-1.rhel7_x86_64.tar.gz cd mongodb-mms-automation-agent-10.2.0.5851-1.rhel7_x86_64 5.2.2 配置 vi local.config mmsGroupId=5d2f52fc9ccf64f3c73b2188 mmsApiKey= #生成ApiKey mkdir /var/lib/mongodb-mms-automation #创建目录,按照配置文件里的路径 mkdir /var/log/mongodb-mms-automation chown `whoami` /var/lib/mongodb-mms-automation #修改归属 chown `whoami` /var/log/mongodb-mms-automation 5.2.3 启动agent nohup ./mongodb-mms-automation-agent --config=local.config >> /var/log/mongodb-mms-automation/automation-agent-fatal.log 2>&1 & 5.3 进行备份5.3.1 备份原理部署了agent后,会将整个副本集的数据同步到MongoDB的数据中心。初始同步完成后,agent会将加密和压缩的oplog数据流式传输到Cloud Manager,以提供数据的连续备份。 5.3.2 开始备份点击start即可开始备份 可以看到oplog一直是延迟极小的 mongodb的备份,默认6小时一次。然后oplog一直备份,秒级延迟。 备份完成后的数据目录:[root@vm4 /backup/replSet27017]#ll -httotal 288K drwxr-xr-x 2 root root 176 Jul 18 16:04 test1 drwxr-xr-x 2 root root 4.0K Jul 18 16:04 test01 drwxr-xr-x 2 root root 4.0K Jul 18 16:04 test drwxr-xr-x 2 root root 172 Jul 18 16:04 mms_test drwxr-xr-x 2 root root 299 Jul 18 16:04 db01 drwxr-xr-x 2 root root 4.0K Jul 18 16:04 admin -rw-r--r-- 1 root root 114 Jul 18 16:04 storage.bson -rw-r--r-- 1 root root 36K Jul 18 16:04 sizeStorer.wt -rw-r--r-- 1 root root 40K Jul 18 16:04 _mdb_catalog.wt -rw-r--r-- 1 root root 373 Jul 18 16:04 restoreInfo.txt -rw-r--r-- 1 root root 755 Jul 18 16:04 seedSecondary.bat -r-x---r-x 1 root root 768 Jul 18 16:04 seedSecondary.sh -rw-r--r-- 1 root root 4.0K Jul 18 16:04 WiredTigerLAS.wt -rw-r--r-- 1 root root 168K Jul 18 16:04 WiredTiger.wt -rw-r--r-- 1 root root 45 Jul 18 16:04 WiredTiger -rw-r--r-- 1 root root 21 Jul 18 16:04 WiredTiger.lock -rw-r--r-- 1 root root 1.2K Jul 18 16:04 WiredTiger.turtle 下载到本地恢复,可以发现oplog应用点和快照点时间基本是一致的: [root@vm4 /backup/replSet27017]#cat restoreInfo.txt Restore Information Group Name: testProject1 Replica Set: replSet27017 Snapshot timestamp: Thu Jul 18 04:12:24 GMT 2019 Last Oplog Applied: Thu Jul 18 04:12:23 GMT 2019 (1563423143, 1) 可以发现没有local库和journal库,所以不能直接恢复加入到副本集里,更适合数据误删恢复。 5.4 进行恢复5.4.1 恢复原理备份后的文件,是物理文件,下载后的数据是备份结束时刻的数据(应用了备份期间oplog)因为mongodb的备份,默认6小时一次。然后oplog一直备份,秒级延迟。所以恢复的时候有了任何时刻的oplog了。恢复有三种选项: 恢复快照(也就是恢复到备份结束时刻) 恢复到oplog timestamp 恢复到point in time(也是转换成oplog timestamp)5.4.2 开始恢复 在线恢复只能恢复到一个新的副本集 只下载备份,就是备份结束时刻;选择oplog点,就是继续应用oplog 按照上图所示选择时间点,会生成一条对应的命令: ./mongodb-backup-restore-util --host --port --rsId replSet27017 --groupId 5d2d88adf2a30bbee892ef0d --opStart 1563423143:1 --opEnd 1563430620:502 --oplogSourceAddr https://api-backup.us-east-1.mongodb.com --apiKey 是直接调用了mongodb官方的API,应用oplog。mongodb-backup-restore-util 是官方提供的恢复工具。 选择point time: ./mongodb-backup-restore-util --host --port --rsId replSet27017 --groupId 5d2d88adf2a30bbee892ef0d --opStart 1563423143:1 --opEnd 1563429600:0 --oplogSourceAddr https://api-backup.us-east-1.mongodb.com --apiKey 发现生成的命令和选择oplog timestamp是一样的,只是把point time转换成oplog timestamp。 seedSecondary.sh文件:因为mongodb cloud manager提供的全备恢复后没有local库,所以会自动生成一个创建oplog的文件。用于创建oplog,插入一条数据,指定副本集复制的点。 [root@vm4 /backup/replSet27017]#cat seedSecondary.sh #!/bin/sh if [ "$#" -ne 4 ]; then echo "Usage: $0 MONGODB_PORT OPLOG_SIZE_GB RS_NAME PRIMARY_HOST:PRIMARY_PORT" exit 1 fi mongo --port ${1} --eval "var res=db.getSiblingDB('local').runCommand({ create: 'oplog.rs', capped: true, size: (${2} * 1024 * 1024 * 1024)}); if(!res.ok){throw res.errmsg;} else{db.getSiblingDB('local').oplog.rs.insert({ts : Timestamp((db.version().match(/^2\.[012]/) ? 1000 : 1) * 1563423143, 1), h : NumberLong('8796150802864664169'), t : NumberLong('113'), op : 'n', ns : '', o : { msg : 'seed from backup service' }});if (db.getSiblingDB('local').system.replset.count({'_id': '${3}'}) == 0) { db.getSiblingDB('local').system.replset.insert({'_id' : '${3}','version' : 1,'members' : [{'_id' : 0,'host' :'${4}'}],'settings' : {}});}}" 二、备份恢复演练 1、还原到副本集 MongoDB运行了一段时间之后,为了新加一个节点,就需要先将备份数据恢复到一个单节点,再将单节点加入到副本集里,下面将演示使用percona server备份的数据进行还原。1.1 得到全部物理备份 replSet27017:SECONDARY> db.runCommand({createBackup: 1, backupDir: "/backup/mongodb_20180722"}) 开始备份后立即开始往新集合里写数据: replSet27017:PRIMARY> for (i = 0; i < 10000; i++){ db.test5.save({'name':'hushi','num':i,date:new Date()}); } WriteResult({ "nInserted" : 1 }) 将备份传输到新的物理机:rsync -r /backup/mongodb_20180722 192.168.1.14:/backup/ 1.2 进行恢复为了加入到副本集,所以启动的时候直接用配置文件启动,将mg27017.conf和mongo.keyfile复制到本地,再将数据文件复制到配置文件设置的目录下。配置文件进行相应的修改,先将副本集相关的参数去掉启动./mongod -f /data1/mongodb27017/mg27017.conf查看恢复之后最后一条非空的oplog > db.oplog.rs.find({op:{$ne:'n'}}).sort({$natural:-1}).limit(1).pretty() { "ts" : Timestamp(1563777601, 5), "t" : NumberLong(123), "h" : NumberLong("-6583634829168454121"), "v" : 2, "op" : "i", "ns" : "config.system.sessions", "ui" : UUID("10b4d6ac-37df-4586-8a2d-83a3389d7406"), "wall" : ISODate("2019-07-22T06:40:01.172Z"), "o" : { "_id" : { "id" : UUID("9805a821-9a88-43bf-a85d-9a0b63b98632"), "uid" : BinData(0,"Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=") }, "lastUse" : ISODate("2019-07-22T06:40:01.175Z"), "user" : { "name" : "root@admin" } } } 发现是系统库的,所以直接查找对test5集合的操作: > db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test5'}).sort({$natural:-1}).limit(1).pretty() { "ts" : Timestamp(1563777479, 674), "t" : NumberLong(123), "h" : NumberLong("6274301431493104273"), "v" : 2, "op" : "i", "ns" : "mms_test.test5", "ui" : UUID("3fd00235-24ea-447a-96c5-2d79071ad9df"), "wall" : ISODate("2019-07-22T06:37:59.619Z"), "o" : { "_id" : ObjectId("5d3559c7dde424d0301c3d21"), "name" : "hushi", "num" : 41015, "date" : ISODate("2019-07-22T06:37:59.623Z") } } 1.3 增备备份因为oplog的特性,多跑一些没有关系,所以上述是直接找到了对test5集合操作的oplog点,现在去找到最新的点。主要要保证上述点和最新的点之间的oplog必须存在。先副本集里找到离现在较近的点:replSet27017:SECONDARY> db.oplog.rs.find({op:{$ne:'n'}}).sort({$natural:-1}).limit(1).pretty() { "ts" : Timestamp(1563780036, 337), "t" : NumberLong(123), "h" : NumberLong("-2687468098839248386"), "v" : 2, "op" : "i", "ns" : "admin.test10", "ui" : UUID("083637d7-6209-4d28-9b4d-529ba26e836c"), "wall" : ISODate("2019-07-22T07:20:36.370Z"), "o" : { "_id" : ObjectId("5d3563c4fc6d8027c7545934"), "name" : "hushi", "num" : 41881, "date" : ISODate("2019-07-22T07:20:36.373Z") } 按照上述的oplog点进行dump: ./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d local -c oplog.rs -o /backup --query='{ts:{$gte:Timestamp(1563777479, 674),$lt:Timestamp(1563780036, 337)}}' 2019-07-22T15:42:40.338+0800 done dumping local.oplog.rs (52140 documents) 2019-07-22T15:42:40.342+0800 dump phase III: the oplog 2019-07-22T15:42:40.342+0800 finishing dump 1.4 将增量进行恢复mv /backup/local/oplog.rs.bson rsync /backup/local/oplog.bson 192.168.1.14:/backup/local/恢复的时候报错: Failed: restore error: error applying oplog: applyOps: not authorized on admin to execute command { applyOps: [ { ts: Timestamp(1563777601, 1), h: 2702590434054582905, v: 2, op: "d", ns: "config.system.sessions", o: { _id: { id: UUID("9f7a5b0b-a1e1-407a-b9dd-7506a3220d9e"), uid: BinData(0, 6399AB0DAC62F20BFC466753B10FB58FB7E692BEC952C69B84D997021794D1F8) } }, o2: {} } ], $db: "admin" } 解决方法(在admin数据库中执行): db.createRole({role:'sysadmin',roles:[], privileges:[ {resource:{anyResource:true},actions:['anyAction']}]}) db.grantRolesToUser( "root" , [ { role: "sysadmin", db: "admin" } ]) 进行恢复 #./mongorestore -vv -h127.0.0.1:27017 -uroot -prootroot --authenticationDatabase admin --oplogReplay /backup/local/ 2019-07-22T16:11:03.901+0800 oplog 7.71MB 2019-07-22T16:11:06.401+0800 applied 51899 ops 2019-07-22T16:11:06.401+0800 oplog 9.13MB 2019-07-22T16:11:06.401+0800 done 数据已经进行了恢复: switched to db admin > db.test10.find().sort({$natural:-1}).limit(1).pretty() { "_id" : ObjectId("5d3563c4fc6d8027c7545933"), "name" : "hushi", "num" : 41880, "date" : ISODate("2019-07-22T07:20:36.373Z") } 如果将单节点以一个新的单节点副本集去启动,那就需要删除掉所有的oplog,再重新启动。那就是跟直接将节点加入副本集是一样的。 > db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'}}).sort({$natural:-1}).limit(1).pretty() { "ts" : Timestamp(1563777601, 5), "t" : NumberLong(123), "h" : NumberLong("-6583634829168454121"), "v" : 2, "op" : "i", "ns" : "config.system.sessions", "ui" : UUID("10b4d6ac-37df-4586-8a2d-83a3389d7406"), "wall" : ISODate("2019-07-22T06:40:01.172Z"), "o" : { "_id" : { "id" : UUID("9805a821-9a88-43bf-a85d-9a0b63b98632"), "uid" : BinData(0,"Y5mrDaxi8gv8RmdTsQ+1j7fmkr7JUsabhNmXAheU0fg=") }, "lastUse" : ISODate("2019-07-22T06:40:01.175Z"), "user" : { "name" : "root@admin" } } } 如果将单节点以一个新的单节点副本集去启动,那就需要删除掉所有的oplog,再重新启动。那就是跟直接将节点加入副本集是一样的。 1.5 加入到副本集 replSet27017:PRIMARY> rs.add({host: "192.168.1.14:27017", priority: 1, hidden: false}) { "ok" : 1, 加入之后,oplog记录也一样了: replSet27017:SECONDARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ts:{$lte:Timestamp(1563780036, 337)}}).sort({$natural:-1}).limit(1).pretty() { "ts" : Timestamp(1563780036, 337), "t" : NumberLong(123), "h" : NumberLong("-2687468098839248386"), "v" : 2, "op" : "i", "ns" : "admin.test10", "ui" : UUID("083637d7-6209-4d28-9b4d-529ba26e836c"), "wall" : ISODate("2019-07-22T07:20:36.370Z"), "o" : { "_id" : ObjectId("5d3563c4fc6d8027c7545934"), "name" : "hushi", "num" : 41881, "date" : ISODate("2019-07-22T07:20:36.373Z") } } 2、恢复误删的单个集合 插入数据for (i = 0; i < 10000; i++){ db.collection1.save({'name':'hushi','num':i}); }collection1集合备份./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d db01 -c collection1-o /data/mongodb/backup再次插入数据(进行一些操作)for (i = 0; i < 1000; i++){ db.collection1.save({'name':'hushi','num':i,date:new Date()}); }误删db.collection1.drop()查找删除 db.oplog.rs.find({op:{$ne:'n'},o:{'drop':'collection1'}}).sort({ts:-1}).pretty().limit(5) { "ts" : Timestamp(1559803854, 1), "t" : NumberLong(44), "h" : NumberLong("-514875429754097077"), "v" : 2, "op" : "c", "ns" : "db01.$cmd", "ui" : UUID("b8fe3553-8249-4f18-b652-5bba86760d43"), "wall" : ISODate("2019-06-06T06:50:54.406Z"), "o" : { "drop" : "collection1" } } 备份oplog ./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d local -c oplog.rs -o /tmp restore的时候需要是oplog.bsonmv /tmp/local/oplog.rs.bson /data/mongodb/backup/oplog.bson 恢复collection1./mongorestore -v -h127.0.0.1:27018-d db01 -c collection1 /data/backup1/db01/collection1.bson查看已经恢复前1w插入记录 db.collection1.count() 10000 应用oplogmongorestore -vv-h127.0.0.1:27018--oplogReplay--oplogLimit=1559803854:1/data/backup1/再次查看插入记录 db.collection1.count() 11000 3、恢复误删的文档 文档误删除 replSet27017:PRIMARY> for (i = 0; i < 1000; i++){ db.test4.save({'name':'hushi','num':i,date:new Date()}); } replSet27017:PRIMARY> db.test4.remove({$and:[{num:{$gte:100,$lte:300}}]}) WriteResult({ "nRemoved" : 201 }) replSet27017:PRIMARY> for (i = 1000; i < 1500; i++){ db.test4.save({'name':'hushi','num':i,date:new Date()}); } WriteResult({ "nInserted" : 1 }) 找到删除开始时间点 replSet27017:PRIMARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test4'},{ts:1,op:1}).sort({$natural:-1}).limit(10).skip(695) { "ts" : Timestamp(1563431039, 6), "op" : "d" } { "ts" : Timestamp(1563431039, 5), "op" : "d" } { "ts" : Timestamp(1563431039, 4), "op" : "d" } { "ts" : Timestamp(1563431039, 3), "op" : "d" } { "ts" : Timestamp(1563431039, 2), "op" : "d" } { "ts" : Timestamp(1563431039, 1), "op" : "d" } { "ts" : Timestamp(1563430620, 502), "op" : "i" } { "ts" : Timestamp(1563430620, 501), "op" : "i" } { "ts" : Timestamp(1563430620, 500), "op" : "i" } { "ts" : Timestamp(1563430620, 499), "op" : "i" } 大于等于这个时间点,删除了201条数据 replSet27017:PRIMARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test4',op:'d',ts:{$gte:Timestamp(1563431039, 1)}}).sort({$natural:-1}).limit(1).count() 201 replSet27017:PRIMARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test4',op:'d',ts:{$gte:Timestamp(1563431039, 1)}}).sort({$natural:-1}).limit(1).pretty() { "ts" : Timestamp(1563431039, 201), "t" : NumberLong(113), "h" : NumberLong("-1345379834581505732"), "v" : 2, "op" : "d", "ns" : "mms_test.test4", "ui" : UUID("9a3dbae7-4f7c-465d-b420-1b7818531fc8"), "wall" : ISODate("2019-07-18T06:23:59.997Z"), "o" : { "_id" : ObjectId("5d300edb42389abced5f7bae") } } replSet27017:PRIMARY> db.getSiblingDB('local').oplog.rs.find({op:{$ne:'n'},ns:'mms_test.test4',op:'d',ts:{$gte:Timestamp(1563431039, 1)}}).sort({$natural:1}).limit(1).pretty() { "ts" : Timestamp(1563431039, 1), "t" : NumberLong(113), "h" : NumberLong("-4777761962883174315"), "v" : 2, "op" : "d", "ns" : "mms_test.test4", "ui" : UUID("9a3dbae7-4f7c-465d-b420-1b7818531fc8"), "wall" : ISODate("2019-07-18T06:23:59.997Z"), "o" : { "_id" : ObjectId("5d300edb42389abced5f7ae6") } } 这里最后试用了一下ongodb-backup-restore-util,如果使用mongorestore是一样的,但是需要先将oplog按照时间点进行dump下来,再去进行恢复。 #./mongodb-backup-restore-util --host 127.0.0.1 --port 27017 --rsId replSet27017 --groupId 5d2d88adf2a30bbee892ef0d --opStart 1563423143:1 --opEnd 1563430620:502 --oplogSourceAddr https://api-backup.us-east-1.mongodb.com --apiKey 5d2f0fbbff7a252987d03c00086f7a1040706ba4c6c634b8f4c09b9e [2019/07/18 19:13:55.843] [pit-restore.debug] [pit-restore/standalone-pit-restore.go:main:116] Creating restore [2019/07/18 19:14:05.406] [pit-restore.debug] [pit-restore/standalone-pit-restore.go:main:137] Successfully completed restore. [2019/07/18 19:14:05.406] [pit-restore.debug] [pit-restore/standalone-pit-restore.go:main:138] Successfully completed restore. [2019/07/18 19:14:05.406] [pit-restore.info] [restore/restore.go:Stop:121] Stopping Restore 已经进行了恢复 db.test4.find({$and:[{num:{$gte:100,$lte:300}}]}).count() 201 三、备份恢复方案 1、备份策略 1.1 MongoDB Cloud Manager的备份策略:The rate is based on your having 28 snapshots at steady state: The six-hour snapshots are kept for two days; The daily snapshots for one week, The weekly snapshots for one month, The monthly for one year. 这是官方提供的全备和全备保留的时间点。oplog是一直备份,秒级延迟。 1.2 全备 + oplog顺着这个思路,全备可以和增备分开,全备照需要,每6个小时或者每一天备份一次。然后oplog每秒或者每5秒或者每1分钟进行一次备份。全备可以使用percona server进行物理热备,oplog可以按照设定的时间间隔一直备份。再可以按将一些重要的文档使用mysqldump进行备份,这样万一有误操作可以快速恢复。 2、增备oplog 2.1 进行增备 #./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d local -c oplog.rs -o /backup --query='{ts:{$gte:Timestamp(1563703200, 1),$lt:Timestamp(1563703500, 1)}}' 这里是增备了5分钟的oplog内容将增备oplog改名:mv oplog.rs.bson oplog.bson.backup ./mongodump -vv -h192.168.1.11:27017 -uroot -prootroot --authenticationDatabase admin -d local -c oplog.rs -o /backup --query='{ts:{$gte:Timestamp(1563771600, 1),$lt:Timestamp(1563775200, 1)}}' 再次增备,将增备的oplog合到一个bson文件里cat oplog.rs.bson >>oplog.bson.backup 接下来继续增备,继续将增备都追加到一个文件里。可以将每一天的增备放到一个文件里。 3、验证备份 进行了备份之后肯定要进行相关的验证,验证分为两块,一个是备份文件恢复后完整性验证,一个是oplog增备是否成功验证。一个思路是每天将全备的文件和增备的文件恢复到一个单节点。3.1 验证全备全备建议使用物理热备,如果使用的是percona server,那么可以WiredTiger.backup文件的最后一条插入记录在恢复的节点上是否存在;如果没有插入记录,或者WiredTiger.backup文件没有内容,可以简单比较一下副本集的集合数据和恢复后的节点是否相同。同样如果使用的是mongodb cloud manager这样的不记录oplog的工具,也是比较一下集合数量。3.2 验证增备验证增备可以查看bson文件里最后一条插入的数据是什么,查看恢复后的文件是否有这条数据。

优秀的个人博客,低调大师

EMR学习笔记(1)HDFS

EMR HDFS Architecture 本文以非HA集群,2个worker的集群为例。 非HA集群,仅有一个Namenode实例,部署在Master节点。Namenode主要职责:-管理文件系统namespace,包括但不仅限于:开、关文件,文件改名,目录操作等。-管控客户端对文件的访问 EMR hadoop集群,每个Worker节点仅部署一个Datanode实例。Datanode主要职责:-管理所在节点挂载的存储-提供给客户端读写服务-block创建、删除以及replication 登录EMR集群实现基本运维 在较新的集群版本中(3.2 以上版本),所有的服务操作都可以通过集群的配置管理功能来完成。推荐优先使用 Web 页面的管理方式。 若您觉得在网页上的作业和执行计划无法满足您更加复杂的应用需求,您可以登录到 E-MapReduce 集群的主机上。找到集群的详情页,其中就有集群 master 机器的公网 IP 地址,您可以直接 SSH 登录到这台机器上,查看各种设置与状态。 登录 Master 主机步骤 使用如下命令 SSH 登录到 master 主机。请在集群详情页的主机信息栏中获取集群 master 机器的公网 IP。ssh root@ip.of.master 输入创建集群时设定的密码。 如何登录 Core 节点A:按照如下步骤: 首先在 Master 节点上切换到 Hadoop 账号:su hadoop 然后即可免密码 SSH 登录到对应的 Core 节点:ssh emr-worker-1 通过 sudo 可以获得 root 权限:sudo vi /etc/hosts 通过命令行方式启停服务进程操作用账号:hdfs NameNode (Master 节点) // 启动 /usr/lib/hadoop-current/sbin/hadoop-daemon.sh start namenode // 停止 /usr/lib/hadoop-current/sbin/hadoop-daemon.sh stop namenode DataNode (Core 节点) // 启动 /usr/lib/hadoop-current/sbin/hadoop-daemon.sh start datanode // 停止 /usr/lib/hadoop-current/sbin/hadoop-daemon.sh stop datanode 示例:登录实际emr集群演示停止datanode进程操作

优秀的个人博客,低调大师

RabbitMQ消息队列学习笔记

概述 初次使用AMQP的过程中,总是容易被AMQP支持的消息模型绕晕,这里结合官方的教程,对AMQP的消息模型做一个简要总结,供参考。目前官方给出了六种消息发送/接收模型,这里主要介绍前五种消息模型。 消息模型 1、Hello World 简单模式就是生产者将消息发送到队列、消费者从队列中获取消息。一条消息对应一个消费者。 示例代码说明: 测试使用的是阿里云的AMQP消息队列服务,具体的代码配置过程可以参考阿里云官方链接。 工具类 import AMQP.AliyunCredentialsProvider; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; public class ConnectionUtil { pu

优秀的个人博客,低调大师

JAVA学习day13

1、finalfinal的意思为最终,不可变。final是个修饰符,它可以用来修饰类,类的成员,以及局部变量。 2、final的特点l final修饰类不可以被继承,但是可以继承其他类。class Yy {}final class Fu extends Yy{} //可以继承Yy类class Zi extends Fu{} //不能继承Fu类l final修饰的方法不可以被覆盖,但父类中没有被final修饰方法,子类覆盖后可以加final。class Fu { // final修饰的方法,不可以被覆盖,但可以继承使用 public final void method1(){} public void method2(){} }class Zi extends Fu { //重写method2方法 public final void method2(){} }l final修饰的变量称为常量,这些变量只能赋值一次。final int i = 20;i = 30; //赋值报错,final修饰的变量只能赋值一次l 引用类型的变量值为对象地址值,地址值不能更改,但是地址内的对象属性值可以修改。final Person p = new Person();Person p2 = new Person();p = p2; //final修饰的变量p,所记录的地址值不能改变p.name = "小明";//可以更改p对象中name属性值p不能为别的对象,而p对象中的name或age属性值可更改。l 修饰成员变量,需要在创建对象前赋值,否则报错。(当没有显式赋值时,多个构造方法的均需要为其赋值。)class Demo { //直接赋值 final int m = 100; //final修饰的成员变量,需要在创建对象前赋值,否则报错。 final int n; public Demo(){ //可以在创建对象时所调用的构造方法中,为变量n赋值 n = 2016; } } 3、static关键字3-1、static特点l 被static修饰的成员变量属于类,不属于这个类的某个对象。(也就是说,多个对象在访问或修改static修饰的成员变量时,其中一个对象将static成员变量值进行了修改,其他对象中的static成员变量值跟着改变,即多个对象共享同一个static成员变量)代码演示:class Demo { public static int num = 100; }class Test { public static void main(String[] args) { Demo d1 = new Demo(); Demo d2 = new Demo(); d1.num = 200; System.out.println(d1.num); //结果为200 System.out.println(d2.num); //结果为200 } }l 被static修饰的成员可以并且建议通过类名直接访问。访问静态成员的格式:类名.静态成员变量名类名.静态成员方法名(参数)对象名.静态成员变量名 ------不建议使用该方式,会出现警告对象名.静态成员方法名(参数) ------不建议使用该方式,会出现警告代码演示:class Demo { //静态成员变量 public static int num = 100; //静态方法 public static void method(){ System.out.println("静态方法"); } }class Test { public static void main(String[] args) { System.out.println(Demo.num); Demo.method(); } } 3-2、static注意事项l 静态内容是优先于对象存在,只能访问静态,不能使用this/super。静态修饰的内容存于静态区。class Demo { //成员变量 public int num = 100; //静态方法 public static void method(){ //this.num; 不能使用this/super。 System.out.println(this.num); } }l 同一个类中,静态成员只能访问静态成员class Demo { //成员变量 public int num = 100; //静态成员变量 public static int count = 200; //静态方法 public static void method(){ //System.out.println(num); 静态方法中,只能访问静态成员变量或静态成员方法 System.out.println(count); } }l main方法为静态方法仅仅为程序执行入口,它不属于任何一个对象,可以定义在任意类中。 4、定义静态常量开发中,我们想在类中定义一个静态常量,通常使用public static final修饰的变量来完成定义。此时变量名用全部大写,多个单词使用下划线连接。定义格式:public static final 数据类型 变量名 = 值;如下演示:class Company { public static final String COMPANY_NAME = "传智播客"; public static void method(){ System.out.println("一个静态方法"); } }当我们想使用类的静态成员时,不需要创建对象,直接使用类名来访问即可。System.out.println(Company.COMPANY_NAME); //打印传智播客Company.method(); // 调用一个静态方法l 注意:接口中的每个成员变量都默认使用public static final修饰。所有接口中的成员变量已是静态常量,由于接口没有构造方法,所以必须显示赋值。可以直接用接口名访问。interface Inter { public static final int COUNT = 100; }访问接口中的静态变量Inter.COUNT 5、匿名对象的概念匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。如:已经存在的类:public class Person{ public void eat(){ System.out.println(); }}创建一个普通对象Person p = new Person();创建一个匿名对象new Person(); 6、匿名对象的特点l 创建匿名对象直接使用,没有变量名。new Person().eat() //eat方法被一个没有名字的Person对象调用了。l 匿名对象在没有指定其引用变量时,只能使用一次。new Person().eat(); 创建一个匿名对象,调用eat方法new Person().eat(); 想再次调用eat方法,重新创建了一个匿名对象l 匿名对象可以作为方法接收的参数、方法返回值使用class Demo { public static Person getPerson(){ //普通方式 //Person p = new Person(); //return p; //匿名对象作为方法返回值 return new Person(); } public static void method(Person p){} }class Test { public static void main(String[] args) { //调用getPerson方法,得到一个Person对象 Person person = Demo.getPerson(); //调用method方法 Demo.method(person); //匿名对象作为方法接收的参数 Demo.method(new Person()); } } 7、内部类在内部类中可以直接访问外部类的所有成员; 7-1、成员内部类成员内部类,定义在外部类中的成员位置。与类中的成员变量相似,可通过外部类对象进行访问l 定义格式class 外部类 { 修饰符 class 内部类 { //其他代码 }}l 访问方式外部类名.内部类名 变量名 = new 外部类名().new 内部类名();l 成员内部类代码演示定义类class Body {//外部类,身体 private boolean life= true; //生命状态 public class Heart { //内部类,心脏 public void jump() { System.out.println("心脏噗通噗通的跳") System.out.println("生命状态" + life); //访问外部类成员变量 } }}访问内部类public static void main(String[] args) { //创建内部类对象 Body.Heart bh = new Body().new Heart(); //调用内部类中的方法 bh.jump(); } 7-2、局部内部类局部内部类,定义在外部类方法中的局部位置。与访问方法中的局部变量相似,可通过调用方法进行访问l 定义格式class 外部类 { 修饰符 返回值类型 方法名(参数) { class 内部类 { //其他代码 } }}l 访问方式在外部类方法中,创建内部类对象,进行访问l 局部内部类代码演示定义类class Party {//外部类,聚会 public void puffBall(){// 吹气球方法 class Ball {// 内部类,气球 public void puff(){ System.out.println("气球膨胀了"); } } //创建内部类对象,调用puff方法 new Ball().puff(); }}访问内部类public static void main(String[] args) { //创建外部类对象 Party p = new Party(); //调用外部类中的puffBall方法 p.puffBall(); } 8、包通常使用公司网址反写,可以有多层包,包名采用全部小写字母,多层包之间用”.”连接类中包的声明格式: package 包名.包名.包名…;如:黑马程序员网址itheima.com那么网址反写就为com.itheima 传智播客 itcast.cn 那么网址反写就为 cn.itcast l 注意:声明包的语句,必须写在程序有效代码的第一行(注释不算)l 代码演示:package cn.itcast; //包的声明,必须在有效代码的第一行import java.util.Scanner;import java.util.Random;public class Demo {} l import导包代码书写的位置:在声明包package后,定义所有类class前,使用导包import包名.包名.类名; 9、访问修饰符在Java中提供了四种访问权限,使用不同的访问权限时,被修饰的内容会有不同的访问权限,以下表来说明不同权限的访问能力: public protected default private同一类中 √ √ √ √同一包中(子类与无关类) √ √ √ 不同包的子类 √ √ 不同包中的无关类 √ 归纳一下:在日常开发过程中,编写的类、方法、成员变量的访问l 要想仅能在本类中访问使用private修饰;l 要想本包中的类都可以访问不加修饰符即可;l 要想本包中的类与其他包中的子类可以访问使用protected修饰l 要想所有包中的所有类都可以访问使用public修饰。l 注意:如果类用public修饰,则类名必须与文件名相同。一个文件中只能有一个public修饰的类。 10、知识点总结l final:关键字,最终的意思 final修饰的类:最终的类,不能被继承 final修饰的变量: 相当于是一个常量, 在编译生产.class文件后,该变量变为常量值 final修饰的方法: 最终的方法,子类不能重写,可以继承过来使用 l static : 关键字, 静态的意思 可以用来修饰类中的成员(成员变量,成员方法) 注意: 也可以用来修饰成员内部类 n 特点: 被静态所修饰的成员,会被所有的对象所共享 被静态所修饰的成员,可以通过类名直接调用,方便 Person.country = "中国"; Person.method(); n 注意事项: 静态的成员,随着类的加载而加载,优先于对象存在 在静态方法中,没有this关键字 静态方法中,只能调用静态的成员(静态成员变量,静态成员方法 l 匿名对象:一个没有名字的对象 n 特点: 创建匿名对象直接使用,没有变量名 匿名对象在没有指定其引用变量时,只能使用一次 匿名对象可以作为方法接收的参数、方法返回值使用 l 内部类:在一个类中,定义了一个新类,这个新的类就是内部类 class A {//外部类 class B{// 内部类 } } n 特点: 内部类可以直接访问外部类的成员,包含私有的成员 l 包的声明与访问 n 类中包的声明格式: package 包名.包名.包名…; n 带有包的类,创建对象格式:包名.类名 变量名 = new包名.类名(); cn.itcast.Demo d = new cn.itcast.Demo(); n 导包的格式: import 包名.类名; l 权限修饰符 public : 公共的 protected: 受保护的 private : 私有的 public protected 默认的 private 在当前类中 Y Y Y Y 同一包中的其他类 Y Y Y 不同包中的子类 Y Y 不同包中的其他类 Y l 代码块: 局部代码块:定义在方法中的,用来限制变量的作用范围 构造代码块:定义在类中方法外,用来给对象中的成员初始化赋值 静态代码块:定义在类中方法外,用来给类的静态成员初始化赋值

优秀的个人博客,低调大师

JAVA学习day12

1、构造方法在创建对象的时候,就有属性值——构造方法 set和get方法时对象创建后使用的 2、什么时候需要写构造方法2-1)当描述的事物在创建其对象时就要明确属性的值,这时就需要在定义类的时候书写带参数的构造方法。2-2)若创建对象时不需要明确具体的数据,这时可以不用书写构造方法(不书写也有默认的构造方法)。 没有写构造方法的:在编译时系统会自动创建一个空的构造方法class Person { //如果没有显示指定构造方法,编译会在编译时自动添加默认的构造方法 //Person(){} //空参数的默认构造方法 } 3、构造方法的格式:修饰符 构造方法名(参数列表){} 4、构造方法的体现: 构造方法没有返回值类型。也不需要写返回值。因为它是为构建对象的,对象创建完,方法就执行结束。 构造方法名称必须和类型保持一致。 构造方法没有具体的返回值。 构造方法的代码体现:class Person { // Person的成员属性age和name private int age; private String name; // Person的构造方法,拥有参数列表 Person(int a, String nm) { // 接受到创建对象时传递进来的值,将值赋给成员属性 age = a; name = nm; } } 5、构造方法的细节:1、一个类中可以有多个构造方法,多个构造方法是以重载的形式存在的2、构造方法是可以被private修饰的,作用:其他程序无法创建该类的对象。class Person { private int age; private String name; // 私有无参数的构造方法,即外界不能通过new Person();语句创建本类对象 private Person() { } // 多个构造方法是以重载的形式存在 Person(int a) { age = a; } Person(String nm, int a) { name = nm; age = a; } } 6、this调用构造方法构造方法之间的调用,可以通过this关键字来完成。l 构造方法调用格式:this(参数列表);l 构造方法的调用class Person { // Person的成员属性 private int age; private String name; // 无参数的构造方法 Person() { } // 给姓名初始化的构造方法 Person(String nm) { name = nm; } // 给姓名和年龄初始化的构造方法 Person(String nm, int a) { // 由于已经存在给姓名进行初始化的构造方法 name = nm;因此只需要调用即可 // 调用其他构造方法,需要通过this关键字来调用 this(nm); // 给年龄初始化 age = a; } } l 注意:this到底代表什么呢?this代表的是对象,具体代表哪个对象呢?哪个对象调用了this所在的方法,this就代表哪个对象。调用其他构造方法的语句必须定义在构造方法的第一行,原因是初始化动作要最先执行。 7、子父类中构造方法的调用在创建子类对象时,父类的构造方法会先执行,因为子类中所有构造方法的第一行有默认的隐式super();语句。格式:调用本类中的构造方法this(实参列表);调用父类中的空参数构造方法super();调用父类中的有参数构造方法 super(实参列表); 为什么子类对象创建都要访问父类中的构造方法?因为子类继承了父类的内容,所以创建对象时,必须要先看父类是如何对其内容进行初始化的,看如下程序:public class Test { public static void main(String[] args) { new Zi(); } }class Fu{ int num ; Fu(){ System.out.println("Fu构造方法"+num); num = 4; } }class Zi extends Fu{ Zi(){ //super(); 调用父类空参数构造方法 System.out.println("Zi构造方法"+num); } }执行结果: Fu构造方法0 Zi构造方法4 通过结果发现,子类构造方法执行时中,调用了父类构造方法,这说明,子类构造方法中有一句super()。那么,子类中的构造方法为什么会有一句隐式的super()呢?原因:子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父类的初始化动作。这样,才可以使用父类中的内容。当父类中没有空参数构造方法时,子类的构造方法必须有显示的super语句,指定要访问的父类有参数构造方法。 8、知识点总结l this关键字n this关键字,本类对象的引用 u this是在方法中使用的,哪个对象调用了该方法,那么,this就代表调用该方法的对象引用 u this什么时候存在的?当创建对象的时候,this存在的 u this的作用:用来区别同名的成员变量与局部变量(this.成员变量) public void setName(String name) { this.name = name; } l 构造方法: 用来给类的成员进行初始化操作n 格式: 修饰符 类名 (参数列表) { ... } n 构造方法的特点: u 1, 方法名与类名相同 u 2,没有返回值,也没有返回值类型,连void也没有 n 构造方法什么时候会被调用执行? 只有在创建对象的时候才可以被调用 l super: 指的是父类的存储空间(理解为父类的引用)调用父类的成员变量: super.成员变量; 调用父类的构造方法: super(参数); 调用方法的成员方法: super.成员方法(); l 继承中的构造方法注意事项: 1,如果我们手动给出了构造方法,编译器不会在给我们提供默认的空参数构造方法 如果我们没写任何的构造方法,编译器提供给我们一个空参数构造方法 2, 在构造方法中,默认的第一条语句为 super(); 它是用来访问父类中的空参数构造方法,进行父类成员的初始化操作 3, 当父类中没有空参数构造方法的时候,怎么办? a: 通过 super(参数) 访问父类有参数的构造方法 b: 通过 this(参数) 访问本类中其他构造方法 注意:[本类中的其他构造方法已经能够正常访问父类构造方法] 4, super(参数) 与 this(参数) 不能同时在构造方法中存在

优秀的个人博客,低调大师

JAVA学习day11

1、接口概述接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。请记住:一切事物均有功能,即一切事物均有接口。 2、接口定义接口定义时需要使用interface关键字定义格式:public interface 接口名 {抽象方法1;抽象方法2;抽象方法3;}使用interface代替了原来的class,其他步骤与定义类相同:l 接口中的方法均为公共访问public的抽象方法l 接口中无法定义普通的成员变量 3、接口中成员变量的定义要求:必须定义为常量固定格式:Public static final 数据类型 变量名 = 值; 4、类与接口的关系实现关系定义类:实现接口,重写接口中的抽象方法类实现接口,可以理解为继承 关键字 implements 称为接口的实现类Class 类 implements 接口{ 重写接口中的抽象方法 } PS:只能创建接口的实现类的对象,不能创建接口的对象 /* 定义接口 使用关键字interface 接口名字 接口定义: 成员方法,全抽象 不能定义带有方法体的方法 定义抽象方法: 固定格式 public abstract 返回值类型 方法名字(参数列表); 修饰符 public 写,或者不写,都是public 接口中成员变量的定义 成员变量的定义,具体要求 要求 : 必须定义为常量 固定格式: public static final 数据类型 变量名 = 值*/ public interface MyInterface { //public static final int a = 1; //在接口中,定义抽象的方法 public abstract void function(); } ================================================/* 定义类, 实现接口,重写接口中的抽象方法 创建实现类的对象 类实现接口, 可以理解为继承 关键字 implements class 类 implements 接口{ 重写接口中的抽象方法 } 类 实现 接口 class MyInterfaceImpl implements MyInterface*/ public class MyInterfaceImpl implements MyInterface{ public void function(){ System.out.println("实现类,重写接口抽象方法"); } } =================================================测试public class Test { public static void main(String[] args) { //创建接口的实现类的对象 MyInterfaceImpl my = new MyInterfaceImpl(); my.function(); } } 5、接口中成员变量的特点6、接口中成员方法的特点 /* 接口中成员的特点 成员变量的特点, 没有变量,都是常量 固定定义格式: public static final 数据类型 变量名 = 值 public 权限 static 可以被类名直接.调用 final 最终,固定住变量的值 // * 注意: public static final 修饰符,在接口的定义中,可以省略不写 但是,不写不等于没有 三个修饰符,还可以选择性书写 接口中的成员方法特点: public abstract 返回值类型 方法名(参数列表) 修饰符 public abstract 可以不写,选择性书写 但是,写不写,都有 实现类,实现接口,重写接口全部抽象方法,创建实现类对象 实现类必须重写所有抽象方法,才能定义类对象,否则仍然是抽象类 实现类,重写了一部分抽象方法,实现类,还是一个抽象类*/ public interface MyInter { public static final int x = 3; public abstract void show(); public abstract int getSum(int a,int b); } 7、接口和抽象类的区别: 抽象类是应该有的接口是额外功能的由于犬分为很多种类,他们吼叫和吃饭的方式不一样,在描述的时候不能具体化,也就是吼叫和吃饭的行为不能明确。当描述行为时,行为的具体动作不能明确,这时,可以将这个行为写为抽象行为,那么这个类也就是抽象类。可是当缉毒犬有其他额外功能时,而这个功能并不在这个事物的体系中。这时可以让缉毒犬具备犬科自身特点的同时也有其他额外功能,可以将这个额外功能定义接口中。如下代码演示:interface 缉毒{ public abstract void 缉毒(); }//定义犬科的这个提醒的共性功能abstract class 犬科{public abstract void 吃饭();public abstract void 吼叫();}// 缉毒犬属于犬科一种,让其继承犬科,获取的犬科的特性,//由于缉毒犬具有缉毒功能,那么它只要实现缉毒接口即可,这样即保证缉毒犬具备犬科的特性,也拥有了缉毒的功能class 缉毒犬 extends 犬科 implements 缉毒{ public void 缉毒() { } void 吃饭() { } void 吼叫() { } }class 缉毒猪 implements 缉毒{ public void 缉毒() { } }相同点:l 都位于继承的顶端,用于被其他类实现或继承;l 都不能直接实例化对象;l 都包含抽象方法,其子类都必须覆写这些抽象方法;区别:l 抽象类为部分方法提供实现,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法;l 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承)l 抽象类是这个事物中应该具备的内容, 继承体系是一种 is..a关系l 接口是这个事物中的额外内容,继承体系是一种 like..a关系二者的选用:l 优先选用接口,尽量少用抽象类;l 需要定义子类的行为,又要为子类提供共性功能时才选用抽象类; 8、多态多态体现为父类引用变量可以指向子类对象。多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。 如Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用 9、接口的多继承多个接口之间可以使用extends进行继承。interface Fu1{ void show(); }interface Fu2{ void show1(); }interface Fu3{ void show2(); }interface Zi extends Fu1,Fu2,Fu3{ void show3(); } 10、extends 继承Implements 实现 11、多态的定义与使用格式多态的定义格式:就是父类的引用变量指向子类对象父类类型 变量名 = new 子类类型();变量名.方法名();l 普通类多态定义的格式父类 变量名 = new 子类();如: class Fu {} class Zi extends Fu {} //类的多态使用 Fu f = new Zi();l 抽象类多态定义的格式抽象类 变量名 = new 抽象类子类();如: abstract class Fu { public abstract void method(); } class Zi extends Fu { public void method(){ System.out.println(“重写父类抽象方法”); } }//类的多态使用Fu fu= new Zi();l 接口多态定义的格式接口 变量名 = new 接口实现类();如: interface Fu { public abstract void method(); }class Zi implements Fu { public void method(){ System.out.println(“重写接口抽象方法”); } }//接口的多态使用Fu fu = new Zi();l 注意事项同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个子类重写后的方法。如 Person p1 = new Student(); Person p2 = new Teacher(); p1.work(); //p1会调用Student类中重写的work方法 p2.work(); //p2会调用Teacher类中重写的work方法当变量名指向不同的子类对象时,由于每个子类重写父类方法的内容不同,所以会调用不同的方法。 12、多态中成员变量的特点 多态中,成员特点 成员变量: 编译的时候, 参考父类中有没有这个变量,如果有,编译成功,没有编译失败 运行的时候, 运行的是父类中的变量值 编译运行全看父类 成员方法: 编译的时候, 参考父类中有没有这个方法,如果有,编译成功,没有编译失败 运行的时候, 运行的是子类的重写方法 编译看父类,运行看子类如下代码 class Fu { int num = 4; }class Zi extends Fu { int num = 5; }class Demo { public static void main(String[] args) { Fu f = new Zi(); System.out.println(f.num);//输出4 Zi z = new Zi(); System.out.println(z.num);//输出5 } }l 多态成员变量 当子父类中出现同名的成员变量时,多态调用该变量时: 编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。 运行时期:也是调用引用型变量所属的类中的成员变量。 简单记:编译和运行都参考等号的左边。编译运行看左边。 13、多态中成员方法的特点class Fu { int num = 4; void show() { System.out.println("Fu show num"); } }class Zi extends Fu { int num = 5; void show() { System.out.println("Zi show num"); } }class Demo { public static void main(String[] args) { Fu f = new Zi(); f.show(); } }l 多态成员方法编译时期:参考引用变量所属的类,如果没有类中没有调用的方法,编译失败。运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员方法。简而言之:编译看左边,运行看右边。 14、instanceof关键字我们可以通过instanceof关键字来判断某个对象是否属于某种数据类型。如学生的对象属于学生类,学生的对象也属于人类。 用于比较引用类型使用格式:boolean b = 对象 instanceof 数据类型; 如 Person p1 = new Student(); // 前提条件,学生类已经继承了人类 boolean flag = p1 instanceof Student; //flag结果为true boolean flag2 = p2 instanceof Teacher; //flag结果为false /* 运算符比较运算符, 结果真假值 关键字, instanceof, 比较引用数据类型 Person p = new Student(); p = new Teacher() 关键字 instanceof 比较, 一个引用类型的变量,是不是这个类型的对象 p变量,是Student类型对象,还是Teacher类型对象 引用变量 instanceof 类名 p instanceof Student 比较,p是不是Student类型的对象,如果是,intanceof返回true */ public class Test { public static void main(String[] args) { Person p = new Student(); // Person p = new Teacher(); boolean b = p instanceof Student; System.out.println(b); //b = p instanceof Animal; //System.out.println(b); p.sleep(); } } 15、多态的向上转型 16、多态的向下转型父类类型强制转换为子类类型,调用子类特有的功能 17、总结下封装、继承、多态的作用:l 封装:把对象的属性与方法的实现细节隐藏,仅对外提供一些公共的访问方式l 继承:子类会自动拥有父类所有可继承的属性和方法。l 多态:配合继承与方法重写提高了代码的复用性与扩展性;如果没有方法重写,则多态同样没有意义。 18、笔记本电脑案例 1. 案例介绍 定义USB接口(具备开启功能、关闭功能),笔记本要使用USB设备,即笔记本在生产时需要预留可以插入USB设备的USB接口,即就是笔记本具备使用USB设备的功能,但具体是什么USB设备,笔记本并不关心,只要符合USB规格的设备都可以。鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,不然鼠标和键盘的生产出来无法使用 进行描述笔记本类,实现笔记本使用USB鼠标、USB键盘 l USB接口,包含开启功能、关闭功能l 笔记本类,包含运行功能、关机功能、使用USB设备功能l 鼠标类,要符合USB接口l 键盘类,要符合USB接口 1. 案例需求分析 阶段一: 使用笔记本,笔记本有运行功能,需要笔记本对象来运行这个功能 阶段二: 想使用一个鼠标,又有一个功能使用鼠标,并多了一个鼠标对象。 阶段三: 还想使用一个键盘 ,又要多一个功能和一个对象 问题:每多一个功能就需要在笔记本对象中定义一个方法,不爽,程序扩展性极差。 降低鼠标、键盘等外围设备和笔记本电脑的耦合性。 1. 实现代码步骤 l 定义鼠标、键盘,笔记本三者之间应该遵守的规则 interface USB { void open();// 开启功能 void close();// 关闭功能 } l 鼠标实现USB规则 class Mouse implements USB { public void open() { System.out.println("鼠标开启"); } public void close() { System.out.println("鼠标关闭"); } } l 键盘实现USB规则 class KeyBoard implements USB { public void open() { System.out.println("键盘开启"); } public void close() { System.out.println("键盘关闭"); } } l 定义笔记本 class NoteBook { // 笔记本开启运行功能 public void run() { System.out.println("笔记本运行"); } // 笔记本使用usb设备,这时当笔记本对象调用这个功能时,必须给其传递一个符合USB规则的USB设备 public void useUSB(USB usb) { // 判断是否有USB设备 if (usb != null) { usb.open(); usb.close(); } } public void shutDown() { System.out.println("笔记本关闭"); } } public class Test { public static void main(String[] args) { // 创建笔记本实体对象 NoteBook nb = new NoteBook(); // 笔记本开启 nb.run(); // 创建鼠标实体对象 Mouse m = new Mouse(); // 笔记本使用鼠标 nb.useUSB(m); // 创建键盘实体对象 KeyBoard kb = new KeyBoard(); // 笔记本使用键盘 nb.useUSB(kb); // 笔记本关闭 nb.shutDown(); } } 19、知识点总结 l 接口:理解为是一个特殊的抽象类,但它不是类,是一个接口 n 接口的特点: 1,定义一个接口用interface关键字 interface Inter{} 2,一个类实现一个接口,实现implements关键字 class Demo implements Inter{} 3, 接口不能直接创建对象 通过多态的方式,由子类来创建对象,接口多态 n 接口中的成员特点: 成员变量: 只能是final 修饰的常量 默认修饰符: public static final 构造方法: 无 成员方法: 只能是抽象方法 默认修饰符: public abstract n 类与类,类与接口,接口与接口之间的关系 类与类之间:继承关系,单继承,可以是多层继承 类与接口之间: 实现关系,单实现,也可以多实现 接口与接口之间:继承关系,单继承,也可以是多继承 Java中的类可以继承一个父类的同时,实现多个接口 l 多态:理解为同一种物质的多种形态 n 多态使用的前提: 1,有继承或者实现关系 2,要方法重写 3,父类引用指向子类对象 n 多态的成员访问特点: 方法的运行看右边,其他都看左边 n 多态的好处: 提高了程序的扩展性 n 多态的弊端: 不能访问子类的特有功能 n 多态的分类 u 类的多态 abstract class Fu { public abstract void method(); } class Zi extends Fu { public void method(){ System.out.println(“重写父类抽象方法”); } } //类的多态使用 Fu fu= new Zi(); u 接口的多态 interface Fu { public abstract void method(); } class Zi implements Fu { public void method(){ System.out.println(“重写接口抽象方法”); } } //接口的多态使用 Fu fu = new Zi(); l instanceof 关键字 格式: 对象名 instanceof 类名 返回值: true, false 作用: 判断指定的对象 是否为 给定类创建的对象

优秀的个人博客,低调大师

JAVA学习day08

1、快捷键 Alt+/ 自动补全 写程序的时候,之写出一部分 输出 syso 类名 Scanner 起名字 如Int 按下alt+/会帮你带出变量 Ctrl+Shift+F 代码格式化 Ctrl+/ 单行注释 取消单行注释就再按一次 Ctrl+Shift+/ 多行注释 Ctrl+Shift+\ 取消多行注释 Ctrl+Shift+o 导入包的快捷键 Alt+上下箭头 移动当前行代码 Ctrl+alt+上下箭头 复制当前代码行 Ctrl+D 删除当前行 Ctrl+1 意见提示功能 2、调试代码 • 断点: 就是一个标记 • 如何设置断点 ○ 代码区域左边的空白位置 双击即可 • 在哪里加 ○ 想看哪个语句的执行结果就在该语句前面加 • 怎么运行断点的程序 ○ 代码区域右键 -- Debug as -- Java Application ○ 显示一个页面,提示是否进入调试页面? 是否记住当前操作?是(Yes)是(勾选复选框) • 弹出了断点页面 ○ 点哪里 § 点击 工具栏中的Step Over (执行下一行) § 快捷键F6 看到每一步程序的执行过程 ○ 看哪里 § 看代码区域: 对照看程序执行步骤 § 看Debug区域: 对照看程序执行步骤 § 看varable区域: 观察变量的创建、复制、销毁 ○ 如何消除断点 § 把设置断点的方式再执行一遍 § 切换到Debug视图,选择 Breakpoints -- Remove All Breakpoints 就是2个XX的图标 3、超市管理系统案例分析 首先需要一个功能菜单,然后输入功能序号后,调用序号对应的功能方法,实现想要的操作。分析步骤如下 ○ 1.完成超市商品初始化。创建商品,将商品添加到集合 ○ 2.显示来到超市能做的操作,也就是显示主菜单 ○ 3.根据接收到的功能选项,执行对应的功能 ○ 3.1.库存货物查询 ○ 3.2.添加新货物 ○ 3.3.删除货物 ○ 3.4.修改货物 ○ 3.5.退出系统,结束main方法的运行 ○ 4.循环,回到 2.显示主菜单 定义FruitItem类 packagecom.ghy.studyday08; /* 自定义类,描述商品信息 商品的属性 编号int 品名Strint 价格double 数量Int 总价double */ publicclassFruitItem{ //商品ID intID; // Stringname; // doubleprice; // intnumber; // doublemoney; } 定义Shopp类 packagecom.ghy.studyday08; importjava.util.ArrayList; importjava.util.Scanner; /* 超市管理系统主页 实现: 1、商品数据的初始化 2、用户的菜单选择 3、根据选择执行不同的功能 3.1Read查看商品 3.2Create创建商品 3.3Delete删除商品 3.4Update更新商品 所有功能,必须定义方法实现 主方法main调用作用 */ publicclassShopp{ publicstaticvoidmain(String[]args){ //创建ArrayList集合,存储商品类型,存输出剧类型FruitItemleixing ArrayList<FruitItem>array=newArrayList<FruitItem>(); //调用商品初始化方法,传递集合 init(array); while(true){ mainMenu(); intchoose=chooseFunction(); switch(choose){ case1: showFruitList(array); break; case2: addFruitItem(array); break; case3: deleteFruit(array); break; case4: updateFruit(array); break; case5: return; default: System.out.println("输入的序号没有"); break; } } } /** *定义方法,实现主菜单 *提示用户哪些选择,选择序号 *返回值:无 *参数:无 * * */ publicstaticvoidmainMenu(){ System.out.println(); System.out.println("===========欢迎光临ITCast超市======================"); System.out.println("1、货物清单2、添加货物3、删除货物4、更新货物5、退出"); System.out.println("请您输入要操作的功能序号"); } /** *定义方法,实现接受用户的键盘输入 *返回编号 */ publicstaticintchooseFunction(){ Scannersc=newScanner(System.in); returnsc.nextInt(); } /** *定义方法,实现显示货物清单的功能 *返回值:无 *参数:集合 *遍历集合,获取集合中的每个FruitItem变量 *变量.调用属性 */ publicstaticvoidshowFruitList(ArrayList<FruitItem>array){ System.out.println(); System.out.println("==========商品库存清单========================="); System.out.println("商品编号商品名称商品单价"); //遍历集合 for(inti=0;i<array.size();i++){ //集合get方法,获取出每个FruitItem的变量,可以使用FruitItem接受get结果 FruitItemitem=array.get(i); //变量item调用类属性 System.out.println(item.ID+""+item.name+""+item.price); } } /** * */ /** *定还是那个方法,实现商品数据的初始化 *先将一部分数据,存储到集合中 *返回值:无 *参数:集合 *方法名:init * */ publicstaticvoidinit(ArrayList<FruitItem>array){ //创建出多个FruitItem类型,并且属性赋值 FruitItemf1=newFruitItem(); f1.ID=9527; f1.name="烧饼"; f1.price=12.7; FruitItemf2=newFruitItem(); f2.ID=9528; f2.name="米粉"; f2.price=22; FruitItemf3=newFruitItem(); f3.ID=9830; f3.name="新疆大盘鸡"; f3.price=100.6; //将创建的3个FriutItem类型的变量,存储到集合中 array.add(f1); array.add(f2); array.add(f3); } /** *定义方法,实现商品的添加功能 *返回值:无 *参数:集合 *提示用户选择的是商品的功能 * *提示用户输入的是什么 * *创建FruitItem变量,变量调用的属性 *将输入的每个商品属性进行赋值 * */ publicstaticvoidaddFruitItem(ArrayList<FruitItem>array){ System.out.println("您选择的是添加商品功能"); //创建Scanner变量 Scannersc=newScanner(System.in); System.out.println("请输入商品的编号"); //输入商品的编号 intID=sc.nextInt(); //输入商品的名字 System.out.println("请输入商品的名字"); Stringname=sc.next(); //输入商品的单价 System.out.println("请输入商品的单价"); doubleprice=sc.nextDouble(); //创建FruitItem的变量 FruitItemitem=newFruitItem(); item.ID=ID; item.name=name; item.price=price; array.add(item); } /* *定义方法,实现商品的修改功能 *返回值:无 *参数: 集合 * *提示用户选择的是修改功能 *提示用户输入需要修改的商品编号 *遍历集合,获取每个FruitItem变量 *变量调用ID属性,属性和用户输入的编号比较 *如果相同: *修改调FruitItem中的属性值 *键盘输入 */ publicstaticvoidupdateFruit(ArrayList<FruitItem>array){ System.out.println("选择的是修改功能"); System.out.println("请输入商品的编号"); Scannersc=newScanner(System.in); intID=sc.nextInt(); //遍历集合,获取每个FruitItem变量 for(inti=0;i<array.size();i++){ FruitItemitem=array.get(i); //获取FruitItem的属性ID,和用户输入的ID比较 if(item.ID==ID){ System.out.println("输入新的商品编号"); item.ID=sc.nextInt(); System.out.println("输入新的商品名字"); item.name=sc.next(); System.out.println("输入新的商品价格"); item.price=sc.nextDouble(); System.out.println("商品修改成功"); return; } } System.out.println("输入的编号不存在"); } /** *定义方法,实现商品的删除功能 *返回值:无 *参数:集合 * *删除依靠的是商品的编号 *提示用户选择的是删除功能 *键盘输入商品的编号 *遍历集合,获取集合中的每个FruitItem变量 *变量调用属性ID,和用户的输入的编号,对比,相同就删除 */ publicstaticvoiddeleteFruit(ArrayList<FruitItem>array){ System.out.println("选择的是删除功能"); System.out.println("请输入商品的编号"); Scannersc=newScanner(System.in); intID=sc.nextInt(); //遍历集合 for(inti=0;i<array.size();i++){ //获取到每个FruitItem变量 FruitItemitem=array.get(i); //变量,调用属性ID,和用户输入的编号比较 if(item.ID==ID){ //移除集合中的元素 //集合的方法remove实现 array.remove(i); System.out.println("删除成功"); return; } } System.out.println("你输入的编号不存在"); } }

优秀的个人博客,低调大师

JAVA学习day07

1、奇数求和练习packagecom.ghy.studyday07; /*要求:计算1-100之间的所有的奇数和1+3+5+7...+99 有1个数据,从0变到100循环int=0<=100++从0-100,范围内,找到奇数数%2==1奇数所有的奇数求和计算需要变量,保存奇数的求和 实现步骤:1.程序可以使用到的数据,预先定义好变量需要求的奇数和2.利用循环,让变量从0变化到1003.判断变量的变化情况是不是奇数4.如果是奇数,和预先定义好的变量,求和*/publicclassLoopTest{publicstaticvoidmain(String[]args){//定义变量,保存求和intsum=0;//for循环,循环中的变量,0-100for(inti=0;i<=100;i++){//对循环的变量,进行奇数的判断,%2==1if(i%2==1){//对奇数求和sum+=i;}}System.out.println(sum); //sum=0; /*for(inti=1;i<=100;i+=2){sum+=i;}System.out.println(sum);*/}} 2、计算出水仙花数即 3位数本身 = 百位数立方 + 十位数立方 + 个位数立方三位数100-999个位数的立方+十位数的立方+百位数的立方=自己本身153=111+555+333已知三位数123获取出每个数位利用除法,取模运算 实现步骤:1.定义变量才存储三个数位上的整数2.利用循环,循环中的变量,从100变化到9993.循环中得到三位数,利用算法,拆解成三个单独数位4.将三个数位立方的求和计算,计算后的求和,和他自己进行比较判断相同,就是水仙花publicclassLoopTest_1{publicstaticvoidmain(String[]args){//定义三个变量intbai=0;intshi=0;intge=0; //循环,循环变量从100-999for(inti=100;i<1000;i++){//对i,进行计算,获取出三个数位//获取百位bai=i/100;//获取十位shi=i/10%10;//获取个位ge=i%10;//对三个数位进行立方求和if(baibaibai+shishishi+gegege==i){System.out.println(i);}}}} 3、ASCII编码表数字0-9对应ASCII编码十进制为48-57, 字母a-z对应ASCII编码十进制为97-122,字母A-Z对应ASCII编码十进制为65-90 4、利用for循环打印字母表 /*利用循环,输出字母包含大写小写,52个输出A-Za-z利用编码表实现,字母和数字的对应关系A-Z65-90a-z97-122 实现步骤:1.定义变量,保存小写a,大写A2.循环26次,输出定义好的变量每次循环,变量++*/publicclassLoopTest_2{publicstaticvoidmain(String[]args){//定义变量,保存2个字母charxiaoXie='a';chardaXie='A';//定义循环,次数26次for(inti=0;i<26;i++){//输出保存字母的变量System.out.println(xiaoXie+""+daXie);daXie++;xiaoXie++;}}} 5、利用for循环打印9*9表for循环的特点: 外循环,行数 内循环,每行的个数 /*利用嵌套for循环,实现99乘法表示实现步骤:1.定义外循环控制行数2.内循环控制个数,个数,每次都在递增3.循环中输出,乘法表的格式1*3=3*/publicclassLoopTest_3{publicstaticvoidmain(String[]args){print99(9);} publicstaticvoidprint99(intn){//定义外循环,循环9次for(inti=1;i<=n;i++){//定义内循环,每次递增,循环条件,<=ifor(intj=1;j<=i;j++){//按照标准格式,输出打印System.out.print(j+""+i+"="+ij+"t");}System.out.println();}}} 6、实现数组的遍历 7、数组的逆序 不等于反向遍历 逆序的实现思想,数组最远端位置的交换 数组的指针思想: 就是数组的索引 指针是可以随时指向数组的任意一个索引的 需要两个指针 一个大指针,一个小指针 大指针=最大索引 小指针=0索引 8、选择排序原理selectSort数组中的每个元素,和其他元素进行比较换位置 元素换位需要借助第三方变量packagecom.ghy.studyday07; /*数组的排序:一般都是升序排列,元素,小到大的排列 两种排序的方式选择排序:数组的每个元素都进行比较冒泡排序:数组中相邻元素进行比较规则:比较大小,位置交换*/publicclassArrayMethodTest_2{publicstaticvoidmain(String[]args){int[]arr={3,1,4,2,56,7,0};//调用选择排序方法//selectSort(arr); //调用冒泡排序方法bubbleSort(arr);printArray(arr);}/*定义方法,实现数组的冒泡排序返回值:没有参数:数组*/publicstaticvoidbubbleSort(int[]arr){for(inti=0;i//每次内循环的比较,从0索引开始,每次都在递减for(intj=0;j//比较的索引,是j和j+1if(arr[j]>arr[j+1]){inttemp=arr[j];arr[j]=arr[j+1];arr[j+1]=temp;}}}} /*定义方法,实现数组的选择排序返回值:没有参数:数组实现步骤:1.嵌套循环实现排序外循环,控制的是一共比较了多少次内循环,控制的是每次比较了多少个元素2.判断元素的大小值小值,存储到小的索引*/publicstaticvoidselectSort(int[]arr){for(inti=0;i//内循环,是每次都在减少,修改变量的定义for(intj=i+1;j//数组的元素进行判断if(arr[i]>arr[j]){//数组的换位inttemp=arr[i];arr[i]=arr[j];arr[j]=temp;}}}} /*定义方法,实现功能返回值:void方法参数:数组*/publicstaticvoidprintArray(int[]arr){//输出一半中括号,不要换行打印System.out.print("[");//数组进行遍历for(inti=0;i//判断遍历到的元素,是不是数组的最后一个元素//如何判断循环变量到达length-1if(i==arr.length-1){//输出数组的元素和]System.out.print(arr[i]+"]");}else{//不是数组的最后一个元素,输出数组元素和逗号System.out.print(arr[i]+",");}}System.out.println();}} 9、冒泡排序功能数组的相邻元素换位置1.嵌套循环实现排序外循环,控制的是一共比较了多少次内循环,控制的是每次比较了多少个元素10、数组的折半查找前提:被查找的数组中的元素,必须有序排列

优秀的个人博客,低调大师

JAVA学习day06

1、3种引用类型 1、数组 2、类 3、接口 2、定义类: 使用类的形式,对现实中的事物进行描述 事物:方法,属性 方法:这个事物具备的功能 属性:变量 格式: Public class 类名{ 属性定义 修饰符 数据类型 变量名 =值 方法定义 修饰符 返回值类型 方法名(参数列表){ } } 3、创建引用类型的变量的格式 数据类型 变量名 =new 数据类型(); 4、实现引用类型的步骤 1、导入包;如果类都是在同一个文件夹,不需要导入包; 2、创建引用类型的变量 3、变量.类型中的功能(也可以调用类中的属性,属性就是变量,赋值和获取值) 测试手机类: /* 定义类: 使用类的形式,对现实中的事物进行描述 事物: 方法,属性 方法: 这个事物具备的功能 属性: 变量 格式: public class 类名{ 属性定义 修饰符 数据类型 变量名 = 值 方法定义 修饰符 返回值类型 方法名(参数列表){ } } 要求: 使用Java中的类,描述生活中的手机这个事物 手机事物,具备哪些属性, 属于他自己的特性 颜色,品牌, 大小 */ public class Phone{ /* 定义手机的属性 */ String color ; String brand ; double size ; } 测试手机类: /* 测试,刚定义好的Phone类 创建引用类型变量的格式 数据类型 变量名 = new 数据类型(); 实现引用类型的步骤 1: 导入包 , 类都是在同一个文件夹,不需要导入包 2: 创建引用类型的变量 3: 变量.类型中的功能 */ public class TestPhone{ public static void main(String[] args){ // 2: 创建引用类型的变量 Phone p = new Phone(); //System.out.println(p); //输出内存的地址 //3: 变量.类型中的功能 //变量 p.的方式,调用类中的属性 //属性就是变量 , 赋值和获取值 p.color = "土豪金"; p.brand = "爱立信"; p.size = 5.0; //获取属性值 System.out.println(p.color+" "+p.brand+" "+p.size); } } 4、程序运行流程 1、方法先进方法区,数据共享区 2、运行时,进入栈 3、引用相关进入堆 5、两个引用类型变量内存图 6、类的练习 电饭锅,包含属性(品牌、容量大小、颜色等) 定义类,描述事物,电饭锅 属性:品牌,大小,颜色 定义类,类名字,电饭锅 类的范围,定义三个属性 7、ArrayList——ArrayList集合的使用 也是引用数据类型 步骤: 1. 导入包 java.util包中 2. 创建引用类型的变量 数据类型< 集合存储的数据类型> 变量名 = new 数据类型 <集合存储的数据类型> (); 集合存储的数据类型: 要将数据存储到集合的容器中 创建集合引用变量的时候,必须要指定好,存储的类型是什么 ArrayList<String> array = new ArrayList<String>(); 3. 变量名.方法 注意: 集合存储的数据,8个基本类型对应8个引用类型 存储引用类型,不存储基本类型 8、ArrayList的常见方法 add(参数) 向集合中添加元素,数据存储进去 方法中的参数类型,定义集合对象时候的类型是一致 ArrayList<Integer> array = new ArrayList<Integer>(); array.add(3); get(int index) 取出集合中的元素,get方法的参数,写入索引 size() 返回集合的长度, 集合存储元素的个数 补充方法: Add(int 索引,存储的元素) 将元素添加到指定的索引上 如:array.add(2,7);//在2索引的位置添加7 set(int 索引,修改后的元素) 将指定的索引上的元素进行修改 Remove(int 索引) 删除指定索引上的元素 Clear() 清空集合中的所有元素 9、ArrayList集合的遍历 /* 集合的遍历 实现思想也是索引思想 集合的索引从0开始,到 size()-1 方法get(int index) */ import java.util.ArrayList; public class ArrayListDemo_2{ public static void main(String[] args){ ArrayList<Integer> array = new ArrayList<Integer>(); array.add(121); array.add(125); array.add(123); array.add(120); array.add(128); //对集合进行遍历 //使用方法 size+get组合进行遍历 for(int i = 0 ; i < array.size(); i++){ System.out.println( array.get(i) ); } } } 10、ArrayList存储的是类型,如商品类型/* 定义,.描述商品的类 商品 4个属性 商品名字 大小 价格 库存 String double double int 定义类,类名Goods 这个类型的变量,存储到集合中 */public class Goods{ //定义商品名字 String brand ; //大小属性 double size ; // 价格属性 double price ; //库存属性 int count ; } /* 实现库存管理案例: 1. 存储商品信息 存储商品类型变量 将商品类型的变量,存储到集合中 2. 查看库存清单 将集合进行遍历, 获取出集合中存储的Goods类型变量 输出每一个Goods类型的属性 计算求和: 总库存,总金额 3. 修改商品的库存 集合遍历 ,获取出集合中存储的Goods类型变量 变量调用Goods类的属性count,值进行修改 (键盘输入) *///import java.util.ArrayList;import java.util.*;public class Shopp{ public static void main(String[] args){ //创建ArrayList集合,存储Goods类型 ArrayList<Goods> array = new ArrayList<Goods>(); //调用添加商品信息的方法 addGoods(array); //进入死循环中 while(true){ //调用选择功能的方法,获取到用户输入的功能序号 int number = chooseFunction(); //对序号判断,如果=1 进入查看库存功能 = 2 进入修改库存功能 =3 结束 switch(number){ case 1: //进入查看库存,调用查看库存的方法,传递存储商品信息的集合 printStore(array); break; case 2: //进入修改库存功能,调用修改库存的方法,传递集合 update(array); break; case 3: return ; default: System.out.println("无此功能"); break; } } } /* 方法定义,修改库存 键盘的输入,将Goods中的属性值,修改 */ public static void update(ArrayList<Goods> array){ Scanner sc = new Scanner(System.in); //遍历集合,获取集合中的每个元素 for(int i = 0 ; i < array.size(); i++){ //集合方法get获取的是集合的元素,元素类型Goods Goods g = array.get(i); System.out.println("请输入"+g.brand+"的库存数"); //Goods属性,count进行修改 g.count = sc.nextInt(); } } /* 定义方法,实现选择菜单,用户根据功能选择菜单 */ public static int chooseFunction(){ System.out.println("-------------库存管理------------"); System.out.println("1.查看库存清单"); System.out.println("2.修改商品库存数量"); System.out.println("3.退出"); System.out.println("请输入要执行的操作序号:"); Scanner sc = new Scanner(System.in); int number = sc.nextInt(); return number; } /* 定义方法,查看库存清单,遍历集合 */ public static void printStore(ArrayList<Goods> array){ //输出表头 System.out.println("----------商场库存清单----------"); System.out.println("品牌型号 尺寸 价格 库存数"); //定义变量,保存总库存数,和总金额 int totalCount = 0 ; double totalMoney = 0; //遍历集合 for(int i = 0 ; i < array.size(); i++){ //get(索引)获取出集合中的元素,存储的是Goods类,获取的也是Goods类型 //使用Goods类型变量,接受get方法结果 Goods g = array.get(i); System.out.println(g.brand+" "+g.size+" "+g.price+" "+g.count); totalCount = totalCount+g.count; totalMoney = totalMoney + g.count*g.price; } System.out.println("总库存数: "+totalCount); System.out.println("商品库存总金额: "+totalMoney); } /* 定义方法,将商品的信息存储到集合中 集合是所有方法的共享数据,参数传递 */ public static void addGoods (ArrayList<Goods> array){ //创建商品类型变量 Goods类型的变量 Goods g1 = new Goods(); Goods g2 = new Goods(); g1.brand = "MacBook"; g1.size = 13.3; g1.price = 9999.99; g1.count = 3; g2.brand = "Thinkpad"; g2.size = 15.6; g2.price = 7999.99; g2.count = 1; //Goods类型的变量,存储到集合中 array.add(g1); array.add(g2); } }

优秀的个人博客,低调大师

JAVA学习day05

1、方法就是用来完成解决某件事件或者实现某个功能的办法。 2、方法的定义格式: 修饰符 返回值类型 方法的名字 (参数列表....){ 方法的功能主体 循环,判断,变量,比较,运算 return ; } 修饰符:固定写法 public static 返回值类型: 方法在运算后,结果的数据类型 方法名: 自定义名字,满足标识符规范,方法名字首字母为小写,后面每个单词首字母大写 参数列表: 方法的运算过程中,是否有未知的数据,如果有未知的数据,定义在参数列表上(定义变量) return: 方法的返回,将计算的结果返回,结束方法 3、调用方法:让一个方法运行起来 4、调用方法执行流程 方法参数里面的都是形参 实际调用时的参数是实参 5、方法调用的内存图 栈:先进后出 后进先出 6、方法定义和使用的注意事项 1、方法不能定义在另一个方法的里面; 2、写错方法名字; 3、写错了参数列表; 4、方法返回值是void,方法中可以省略return不写; Return 后面不能有代码; 5、方法返回值类型,和return 后面数据类型必须匹配; 6、方法重复定义问题; 7、调用方法的时候,返回值是void,不能写在输出语句中; 7、方法的重载 方法的重载特性(overload) 在同一个类中,允许出现同名的方法,只要方法的参数列表不同即可,这样方法就是重载 参数列表不同:参数的个数,数据类型,顺序 对于重载的调用,根据参数传递进行区分 8、方法重载的注意事项 1、参数列表必须不同 2、重载和参数变量名无关 3、重载和返回值类型无关 4、重载和修饰符无关 技巧:重载看方法名和参数列表 9、方法参数是基本数据类型 方法,调用中的参数传递问题 1、方法参数是基本数据类型 2、方法参数是引用类型:传递的是内存地址 10、库存练习package com.ghy.study; /* 实现商品的库存管理 功能: 1. 展示用户选择功能清单 2. 根据选择的功能编号,进行不同的操作 A. 展示所有库存 B. 修改库存数量 分析: 1. 展示用户清单: 输出语句, 用户输入, 选择功能序号 2. 根据选择,调用不同的方法 switch语句 case 1 2 3 A 展示库存 将存储商品的数组,遍历 B 修改库存 修改所有的库存数量 */import java.util.Scanner; public class ShopMethod { public static void main(String[] args){ //使用数组,保存商品的信息 //品名,尺寸,价格,库存数, 定义5个数组 String[] brand = {"MacBookAir","ThinkpadT450"}; double[] size = {13.3,15.6}; double[] price = {9998.97,6789.56}; int[] count = {0,0}; while(true){ int choose = chooseFunction(); switch(choose){ case 1: //调用查看库存清单方法 printStore(brand,size,price,count); break; case 2: //调用修改库存的方法 update(brand,count); break; case 3: return ; default: System.out.println("没有这个功能"); break; } } } /* 定义方法,修改所有商品的库存 用户输入1个,修改1个 返回值,没有 参数, 库存数的数组, 品名数组 */ public static void update(String[] brand, int[] count){ //遍历数组,遍历到一个,修改一个 //接受键盘输入 Scanner sc = new Scanner(System.in); //遍历数组 for(int i = 0; i < brand.length ; i++){ System.out.println("请输入"+brand[i]+"的库存数"); //键盘输入,录入库存, 存储到库存的数组中 int newCount = sc.nextInt(); count[i] = newCount; } //int chooseNumber = sc.nextInt(); } /* 定义方法,展示所有的库存清单,遍历 返回值,没有 参数, 数组 */ public static void printStore(String[] brand,double[] size,double[] price,int[] count){ System.out.println("----------商场库存清单----------"); System.out.println("品牌型号 尺寸 价格 库存数"); //定义变量,计算总库存数,和总价格 int totalCount = 0; int totalMoney = 0; //遍历数组,将数组中所有的商品信息打印出来 for(int i = 0 ; i < brand.length ; i++){ System.out.println(brand[i]+" "+size[i]+" "+price[i]+" "+count[i]); totalCount += count[i]; totalMoney += count[i]*price[i]; } System.out.println("总库存数: "+totalCount); System.out.println("商品库存总金额: "+totalMoney); } /* 定义方法,实现用户的选择功能,功能的需要返回来 返回值, int 参数, 没有 */ public static int chooseFunction(){ System.out.println("-------------库存管理------------"); System.out.println("1.查看库存清单"); System.out.println("2.修改商品库存数量"); System.out.println("3.退出"); System.out.println("请输入要执行的操作序号:"); //接受键盘输入 Scanner sc = new Scanner(System.in); int chooseNumber = sc.nextInt(); return chooseNumber; } }

优秀的个人博客,低调大师

JAVA学习day04

1、选择语句switch编写格式Switch( 表达式 ){ Case 常量1: 要执行的语句; Break; Case 常量2: 要执行的语句; Break; Case 常量3: 要执行的语句; Break; Default: 要执行的语句; Break; }执行流程:表达式,和case后面的常量进行比较和哪个case后的常量相同,就执行哪个case后面的程序,遇到break,就全部结束; 2、case的穿透性案例:星期1-5 工作日 星期6-7 输出休息日case 1 case 2 case 3 case 4 case 5 输出结果相同 case后面的常量,和switch中数据相同,没有break,一直向下穿透case后面没有break,程序就会一直向下穿透 3、数组数据多了存数组(原因:数据太多,定义变量,容易造成代码重复) 定义数组容器 定义数组容器,要素,强制数据类型的语言 必须要有数据类型,大小,就是存储数据的个数 定义数组公式: 数组类型 [] 变量名 = new 数据类型 [存储元素的个数] 数据类型:数组中存储元素的数据类型 [] 表示数组的意思 变量 自定义标识符 New 创建容器关键字 数据类型:数组中存储元素的数据类型 [] 表示数组的意思 元素个数,就是数组中,可以存储多少个数据(恒定,数组是定长型) 数组是一个容器:存储到数组中的每个元素,都有自己的额自动编号 自动编号,最小值是0,最大值,长度-1 自动编号专业名词,索引(index),下表,角标 访问数组存储的元素,必须依赖于索引,公式 数组名[索引] JAVA提供了一个属性,操作索引的 数组的一个属性,就是数组的长度,属性的名字 Length 使用属性: 数组名.length 数据类型 Int 数组的最小索引是0 最大索引是数组.length-1 4、JVM的内存划分 5、 其中:arr引用类型,保存的数据,实质上是内存中的地址; 6、为数组的元素赋值Arr[1]=5 7、数组的定义数组的两个定义方式 数据类型[] 变量名 = new 数据类型[]{元素1,元素2,元素3} ; Int[] arr =new int[] {1,2,3,4,5,6,7} 数据类型[] 变量名 ={匀速1,元素2,元素3}; //定义数组,直接大括号赋值这种方式推荐使用 int[]arr2={1,2,3,4,5,8}; System.out.println(arr2.length);//打印数组的长度 System.out.println(arr2[5]);//索引从0开始 8、数组的遍历 通过索引的方式将数组中的每个元素,分别获取出来 要依靠索引才能实现 //如下方式是遍历数组最常用的方式 for(inti=0;i<arr.length;i++){ System.out.println(arr[i]); } 9、数组中常见的异常 数组的索引越界异常 空指针异常 10、数组获取最值得原理思想 1)定义变量,保存在数组中 Int max =arr[0]; 2)max和数组中的其他元素进行比较 A 数组遍历 B max比其他索引上的元素小 max记录下较大的数 3)数组遍历完成后 max记录的就是数组的最大值 publicclassArrayDemo_5{ publicstaticvoidmain(String[]args){ //定义数组,Intleixing int[]arr={5,2,3,5,8,9,0,4}; //定义变量,记录数组中0索引上的元素 intmax=arr[0]; //遍历数组,获取所有的元素,和变量max比较 for(inti=1;i<arr.length;i++){ //变量max,和数组中的每个元素进行比较 //如果Max小于了数组中的一个元素,将较大的元素赋值给max if(arr[i]>max){ max=arr[i]; } } //遍历完成,变量max就是数组的最大值 System.out.println(max); } } 11、二维数组的定义 11、二维数组的定义和访问 12、二维数组的遍历 13、维数组的遍历求和 /* 二维数组的遍历求和 第一小组销售额为{11,12}万元 第二小组销售额为{21,22,23}万元 第三小组销售额为{31,32,33,34}万元。 每个小组就是一个数组,三个数组存储到一个数组中,形成二维数组 求和: 每个小组求和 所有元素求和 */ publicclassArrayArrayDemo_3{ publicstaticvoidmain(String[]args){ int[][]arr={{11,12},{21,22,23},{31,32,33,34}}; //定义变量,保存2个求和的值 intsum=0;//所有元素的求和 intgroupSum=0;//每个一维数组的求和 for(inti=0;i<arr.length;i++){ for(intj=0;j<arr[i].length;j++){ //将一维数组的元素求和 groupSum+=arr[i][j]; } System.out.println("每个小组总金额"+groupSum); //将每个一维数组的总和在相加 sum+=groupSum; //每个唯一数组求和,清空 groupSum=0; } System.out.println("本公司总金额"+sum); } } 14、随机点名器 packagecom.ghy.study; /* 随机点名器: 1存储姓名 2.预览所有人的姓名 3.随机出一个人的姓名 */ importjava.util.Random; publicclassCallName{ publicstaticvoidmain(String[]args){ //存储姓名,姓名存储到数组中 //数组存储姓名,姓名的数据类型,String String[]names={"张三","李四","王五","李蕾","韩梅梅","小名","老王","小华","约翰逊","爱丽丝"}; //预览:遍历数组,打印所有姓名 for(inti=0;i<names.length;i++){ System.out.println(names[i]); } System.out.println(names.length); System.out.println("============="); //随机出一个人的名 //利用随机数,生成一个整数,作为索引,到数组中找到对应的元素 Randomran=newRandom(); //随机数,范围必须是0-数组的最大索引 //nextInt是0-这个数,但不包含这个数 intindex=ran.nextInt(names.length);//index就是随机数,作为索引 System.out.println(names[index]); } }

优秀的个人博客,低调大师

JAVA学习day03

1、引用类型变量格式:类型 变量名=new 类型();举例:Scanner sc=new Sccanner();每个引用类型,都有自己的功能,如何使用功能格式:变量.方法名() 2、util 工具的意思 3、Scanner类,作用,让我们在命令行中,接收键盘的输入 使用Scanner类的步骤: 1、导入包,知名类所在的文件夹 java文件夹的util文件夹 2、公式,创建出Scanner类型变量 3、变量.使用Scanner类中的功能,完成键盘输入 4、随机数java中已经有的引用类型Random类步骤:1、导入包,Random类,在java.util文件夹2、公式:创建Random类型的变量3、变量.调用Random类中的功能,产生随机数 Random类,提供功能,名字,nextInt()产生一个随机数,结果是Int类型出现随机数的范围,在功能netxint()(写一个整数),整数:随机出来的范围随机数的范围0-指定的整数间的随机数nextInt(100)0-99 5、if语句 /***if语句*编写格式:*if(条件){*if语句的执行体*}*条件:结果必须是布尔类型1==1true||false*执行体:当if中的条件为true的时候,执行大括号中的执行体*if条件是false的时候,不执行****/publicclassIfDemo{publicstaticvoidmain(String[]args){inti=5;if(i==5){System.out.println("if中的条件是true");i++;}System.out.println(i); } } 6、if…else语句 7、三元运算符判断条件?表达式1:表达式2如果条件为真,则表达式1;如果为假,则表达式2 8、while循环编写格式:While(条件){ 循环体 }条件:当条件是true,就执行循环体,执行虚幻提后程序再次执行whie中德条件,如果条件还是true,继续执行循环体,直到条件是false的时候,循环就结束。 9、for循环编写格式For(初始化变量;条件;增量){ 循环体 } 10、do…while循环编写格式: Do{ 循环体 }while(条件) 特点:无条件先执行一次 11、嵌套循环循环里面还有虚幻,for形式多For(){ For(){ } }总循环次数=外循环次数*内循环次数 内循环是外循环的循环体 外循环,控制的是行数内循环,控制的是每行的个数 12、跳出break关键字 作用于循环中,终止循环的作用continue关键字 作用:在循环中,终止本次循环,开始下一个循环 13、猜数字游戏 package com.ghy.study; import java.util.Random;import java.util.Scanner; /** 实现猜数字的小游戏 随机数一个数字,让用户猜 结果3种情况* 随机数:Random 键盘输入:Scanner 猜的数字,和随机数进行比较if判断 直到猜中为止,反复去猜,循环while*/ public class GuestNumber { public static void main(String[] args) { System.out.println("猜数字游戏开始啦"); Random r=new Random(); Scanner sc=new Scanner(System.in); int j = r.nextInt(10); System.out.println(j); while (true) { System.out.println("请输入数据:"); int i = sc.nextInt(10); if(i == j){ System.out.println("恭喜你猜对了"); break; }else if(j > i){ System.out.println("猜小了"); }else if (j < i){ System.out.println("猜大了"); } } } }

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册